vue3+qiankun框架初体验

项目框架基座和子项目都为vue3

qiankun官网

首先我们需要了解什么是微前端,他是做什么的。

1.什么是微前端
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。简单来说就是就是可以把多个不同框架的项目部署在不同环境,但又可以在一个项目中访问。
2.他是做什么的
他可以将不同框架的多个项目组成统一项目,而组成项目的子项目又可以单独部署单独访问。可以说他们的关系是互相关联而又互不关联。

说明:由于项目还并没有进行上线测试,此代码还不能保证上线没有问题,如有问题请与我交流沟通。
主项目代码实现
1.首先我们需要建立.env.development,.env.production,.ent.qa。3个文件,这3个文件的作用是为了配置前端打包项目路径配置,方便上线,测试,开发环境的区分(注意:文件内配置必须为VUE_APP_开头,否则后面用到的时候不能进行解析)
env.development

NODE_ENV = 'development'
// 开发主项目
VUE_APP_BASE_URL = 'http://localhost:8080'
// 开发子项目
VUE_APP_APP_URL = 'http://localhost:8081'
VUE_APP_SERVICEMESH_URL = 'http://localhost:8082'
VUE_APP_RESOURCE_URL = 'http://localhost:8083'
VUE_APP_MONITOR_URL = 'http://localhost:8084'
VUE_APP_SYSTEM_URL = 'http://localhost:8085'

env.production

NODE_ENV = 'production'
// 生产主项目
VUE_APP_BASE_URL = 'http://localhost:8080'
// 生产子项目
VUE_APP_APP_URL = 'http://localhost:8081'
VUE_APP_SERVICEMESH_URL = 'http://localhost:8082'
VUE_APP_RESOURCE_URL = 'http://localhost:8083'
VUE_APP_MONITOR_URL = 'http://localhost:8084'
VUE_APP_SYSTEM_URL = 'http://localhost:8085'

env.qa

NODE_ENV = 'qa'
// 测试主项目
VUE_APP_BASE_URL = 'http://localhost:8080'
// 测试子项目
VUE_APP_APP_URL = 'http://localhost:8081'
VUE_APP_SERVICEMESH_URL = 'http://localhost:8082'
VUE_APP_RESOURCE_URL = 'http://localhost:8083'
VUE_APP_MONITOR_URL = 'http://localhost:8084'
VUE_APP_SYSTEM_URL = 'http://localhost:8085'

package.json增加

"scripts": {
    "serve": "vue-cli-service serve ",
    "build": "vue-cli-service build ",
    "lint": "vue-cli-service lint",
    "serve-qa": "vue-cli-service serve --mode qa",
    "serve-product": "vue-cli-service serve --mode production",
    "build-qa": "vue-cli-service build --mode qa",
    "build-product": "vue-cli-service build --mode production"
  },

主项目router.js

import {
    createRouter,
    createWebHashHistory
} from 'vue-router';
import store from '../store/store';
// 采用路由懒加载方式
const login = () => import('../components/static/Login.vue');
const Home = () => import('../components/static/Home.vue');
const router = createRouter({
    history: createWebHashHistory(),
    routes: [{
        path: '/',
        redirect: login,
        name: '登录'
    }, {
        path: '/login',
        component: login,
        name: '登录'
    }, {
        path: '/home',
        name: 'Home',
        component: Home
    }]
});
export default router;

主项目src下创建micro文件夹,
micro/apps.js 用来配置子项目路由

// /src/micfo/apps.js
//process.env为上边配置的路径第值
console.log(process.env);
const apps = [{
    name: 'kem-app-name',
    entry: process.env.VUE_APP_APP_URL,
    container: '#KEMAPP',//承载子项目的div的id值必须与此对应
    activeRule: '#/kem-app',//激活子项目的路由
    sandbox: {
        strictStyleIsolation: true // 开启样式隔离
    }
}];

export default apps;

mico/index.js

// src/mico/index.js
import {
    registerMicroApps,
    addGlobalUncaughtErrorHandler,
    start,
    initGlobalState
} from 'qiankun';

import apps from './apps';

registerMicroApps(apps, {
    beforeLoad: [
        app => {
            console.log("before load", app.name);
            return Promise.resolve();
        },
    ],
    beforeMount: [
        app => {
            console.log("before mount", app.name);
            return Promise.resolve();
        },
    ],
    afterUnmount: [
        app => {
            console.log("after mount", app.name);
            return Promise.resolve();
        },
    ],
}, );
const state = {};
//主项目与子项目交互用的参数,子项目与主项目都可以修改此参数
const actions = initGlobalState(state);
actions.setGlobalState({
    globalToken: ''
})
addGlobalUncaughtErrorHandler((event) => {
    console.log(event);
    const {
        msg
    } = event;
    if (msg && msg.includes('died in status LOADING_SOURCE_CODE')) {
        console.log('微应用加载失败,请检查应用是否可运行');
    }
});

export default start;
export {
    actions
}

主项目的App.vue

<template>
  <div>
    <a-layout v-if="token" style="min-height: 100vh">
      <a-layout-header>
        <div class="titleBox">
          123
        </div>
      </a-layout-header>
      <a-layout>
        <a-layout-sider v-model:collapsed="collapsed" collapsible>
          <a-menu
            v-model:selectedKeys="selectedKeys"
            theme="dark"
            mode="inline"
            :open-keys="openKeys"
            @openChange="onOpenChange"
          >
            <a-menu-item key="dashboard">
              <HeartOutlined />
              <span>dashboard</span>
            </a-menu-item>
            <a-sub-menu v-for="item in menu" :key="item.key">
              <template #title>
                <span>
                  <ReconciliationOutlined
                    v-else-if="item.title == 'dashboard'"
                  />
                  <HeartOutlined v-else />
                  <span>{{ item.title }}</span>
                </span>
              </template>
              <a-menu-item
                v-for="itemchildren in item.children"
                :key="itemchildren.title"
              >
                <router-link :to="itemchildren.url">{{
                  itemchildren.title
                }}</router-link>
              </a-menu-item>
            </a-sub-menu>
          </a-menu>
        </a-layout-sider>
        <a-layout-content>
          <router-view />
          // 此项的值与micfo/apps.js中apps中container的值对应
          <div id="KEMAPP"></div>
        </a-layout-content>
      </a-layout>
    </a-layout>
    <router-view />
  </div>
</template>

<script>
import { useStore } from 'vuex';
import {
  AppstoreOutlined,
  DatabaseOutlined,
  FileSearchOutlined,
  ToolOutlined,
  SettingOutlined,
  ReconciliationOutlined,
  HeartOutlined
} from '@ant-design/icons-vue';
import {
  defineComponent,
  ref,
  computed,
  // onMounted,
  toRefs,
  reactive
} from 'vue';
// import { useRouter } from "vue-router";
import { logout } from '@/commons/common';
export default defineComponent({
  name: 'App',
  components: {
    AppstoreOutlined,
    DatabaseOutlined,
    FileSearchOutlined,
    ToolOutlined,
    SettingOutlined,
    ReconciliationOutlined,
    HeartOutlined
  },
  setup() {
    const store = useStore();
    // 在页面刷新时将vuex里的信息保存到sessionStorage里
    window.addEventListener('beforeunload', () => {
      sessionStorage.setItem('store', JSON.stringify(store.state));
    });
    // const router = useRouter();
    const state = reactive({
      openKeys: ['']
    });
    const menu = ref([
      {
        title: '系统',
        key: 'app',
        children: [
          { title: '应用', url: '/kem-app/about' }
        ]
      }
    ]);
    const onOpenChange = openKeys => {
      console.log(openKeys);
      const latestOpenKey = openKeys.find(
        key => state.openKeys.indexOf(key) === -1
      );
      if (openKeys.length == 0) {
        state.openKeys = openKeys;
      } else {
        state.openKeys = latestOpenKey ? [latestOpenKey] : [];
      }
    };
    return {
      collapsed: ref(true),
      selectedKeys: ref([]),
      menu,
      token: computed(() => store.state.token),
      logout,
      ...toRefs(state),
      onOpenChange
    };
  }
});
</script>

子项目

子项目 vue.config.js

const path = require('path');
const packageName = require('./package.json').name;
console.log(packageName);

function resolve(dir) {
    return path.join(__dirname, dir);
}
const port = 8081; // dev port
const dev = process.env.NODE_ENV === 'development'

module.exports = {
    /**
     * You will need to set publicPath if you plan to deploy your site under a sub path,
     * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
     * then publicPath should be set to "/bar/".
     * In most cases please use '/' !!!
     * Detail: https://cli.vuejs.org/config/#publicpath
     */
    publicPath: dev ? `//localhost:${port}` : '/',
    outputDir: 'dist',
    chainWebpack: config => {
        // 删除 浏览器在⻚⾯加载完成后,利⽤空闲时间提前获取⽤户未来可能会访问的内容。
        config.plugins.delete('prefetch');
        // 压缩代码
        config.optimization.minimize(true);
        config.module.rule('images')
            .use('url-loader')
            .loader('url-loader')
            .options({
                limit: 4096, // ⼩于4kb将会被打包成 base64
                fallback: {
                    loader: 'file-loader',
                    options: {
                        name: './img/[name].[hash:8].[ext]',
                        publicPath: dev ? `//localhost:${port}` : '/'
                    }
                }
            })
        config.module
            .rule('md')
            .test(/\.md/)
            .use('text-loader')
            .loader('text-loader')
            .end()
    },
    assetsDir: 'static',
    filenameHashing: true,
    // tweak internal webpack configuration.
    // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
    devServer: {
        // host: '0.0.0.0',
        hot: true,
        disableHostCheck: true,
        port,
        overlay: {
            warnings: false,
            errors: true,
        },
        headers: {
            'Access-Control-Allow-Origin': '*',
        },
    },
    // 自定义webpack配置 packageName的值为package.json中name值
    configureWebpack: {
        resolve: {
            alias: {
                '@': resolve('src'),
            },
        },
        output: {
        // 该值需要与主项目中micfo/apps.js apps中的name值相对应
            library: `${packageName}-name`,
            libraryTarget: 'umd',
            jsonpFunction: `webpackJsonp_${packageName}`,
        },
    },
};

子项目src路径下创建2个文件夹shares和micro
shares/action.js

// 主应用与子应用字段交互方法
function emptyAction() {
    // 警告:提示当前使用的是空 Action
    console.warn("Current execute action is empty!");
}

class Actions {
    // 默认值为空 Action
    actions = {
        onGlobalStateChange: emptyAction,
        setGlobalState: emptyAction
    };

    /**
     * 设置 actions
     */
    setActions(actions) {
        this.actions = actions;
    }

    /**
     * 映射
     */
    onGlobalStateChange(...args) {
        return this.actions.onGlobalStateChange(...args);
    }

    /**
     * 映射
     */
    setGlobalState(...args) {
        return this.actions.setGlobalState(...args);
    }
}

const actions = new Actions();
export default actions;

micro/public-path.js

//  /src/micro/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
    // eslint-disable-next-line no-undef
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

完毕

你可能感兴趣的:(vue,qiankun,微前端)