自学内容网 自学内容网

【qiankun微前端】已有工程改造微前端

最初接触微前端有种高大上的感觉,细细研究了一下,其实也不难,本文就不介绍iframe,single-spa之类的东西了,直奔主题。

当前系统有横向一级顶部路由和竖向二级侧面路由,点击一级路由会触发二级路由的切换,从而实现两级路由的切换。

之所以采用微前端,是因为我们的工程有太多冗余第三方代码,因此为第三方用户开辟一个单独的工程势在必行,所以说微前端也是为了我们主体仓库代码的干净。

主应用路由(router.js)配置,增加sub项

        {
            path: "/micro/*",
            name: "micro",
            component: Home,// Home一般为包含上部和左侧路由的layout组件
            redirect: "/",
            meta: {
                auth: false, // 是否需要登录
                keepAlive: false // 是否缓存组件
            },
            children: [{
                path: "/",
                name: "sub",
                component: () => import('@/views/sub.vue'),// 当路由点击时,链接到sub组件
                meta: {
                    auth: true,
                    keepAlive: false
                }
            }]
        },

主应用sub.vue(当路由点击时,打开的子应用页面)

<template>
  <div class="layout">
    <Layout class="main_view">
    <!-- micros为定义的微应用挂载dom的id -->
        <div id="micros" class="views"></div>
    </Layout>
  </div>
</template>

<script>
import '@/micros' // 触发微应用的注册和主应用的数据监听等逻辑
import {start} from "qiankun";
import API from '@/api/apv';
import {
    mapActions
} from "vuex";

export default {
  name: "micros",
  created() {
    start()
  },
  mounted() {
      this.signingReminder()
      this.$nextTick(()=>{
       // 此逻辑为了解决主应用在子应用路由下直接刷新导致的路由不展示的问题;
       // 当前子页面的mounted周期,对左侧路由进行store数值更新,并进行refreshList刷新操作(这里的逻辑视自己工程具体情况而定,核心思路是一致的)。
        this.comeSetListArrQiankun(
          [
            {
                "name": "仓库管理",
                "path": "/micro/thirdPartyStore",
                "showChild": 0,
                "tabType": 600,
                "type": "third_party_supplier",
                "child": [
                    {
                        "name": "入库管理sub",
                        "path": "/micro/thirdPartyStore/stock",
                        "signColor": 6000,
                        "type": "third_party_supplier",
                        "child": 1
                    },
                    {
                        "name": "库存管理sub",
                        "path": "/micro/thirdPartyStore/inventory",
                        "signColor": 6001,
                        "type": "third_party_supplier",
                        "child": 1
                    },
                    {
                        "name": "出库管理sub",
                        "path": "/micro/thirdPartyStore/outbound",
                        "signColor": 6002,
                        "type": "third_party_supplier",
                        "child": 1
                    },
                ]
            }
          ]
        )
        // 路由更新需要主动触发refreshList,刷新视图
        this.$parent.$parent.$refs.PageHeader.$emit('refreshList')
      })
  },

  methods: {
    ...mapActions("index",['comeSetListArrQiankun']),
    signingReminder() {
        // 当前页面的其他逻辑
    },
  }
}
</script>

micros中的apps.js

import store from '@/store/index'
import {registerMicroApps} from "qiankun";
const apps = [
    /**
     * name: 微应用名称 - 具有唯一性
     * entry: 微应用入口 - 通过该地址加载微应用
     * container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
     * activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
     */
    {
        name: "micro",
        entry: "http://127.0.0.1:8889/sub/micro/",
        container: "#micros",
        activeRule: "/mono/micro/",
        props: {
        }
    },
];
export default apps;

micros中的index.js

import { registerMicroApps, initGlobalState, addGlobalUncaughtErrorHandler, start } from "qiankun";
import apps from "@/micros/apps";
import store from '../store/index'
/**
 * @description: 注册微应用
 * 第一个参数 - 微应用的注册信息
 * 第二个参数 - 全局生命周期钩子
 */
registerMicroApps(apps,
    {
        beforeLoad: [
            async (app) => {
            }
        ],
        beforeMount: [
            app => {
                //console.log("subdata",app)
                //console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
            },
        ],
        afterMount: [
            app => {
                //console.log("subdata",window)
                //console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
            },
        ],
        afterUnmount: [
            app => {
                //console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name);
                //location.reload(true)
            },
        ],
    }
);

// 初始化 state
const actions = initGlobalState({menus: []});
// 主应用监听路由列表传递,一旦传递过来通过主应用自身的store进行路由设置
actions.onGlobalStateChange((state, prev) => {
 // state: 变更后的状态; prev 变更前的状态
 store.dispatch('index/comeSetListArrQiankun', state.menus);
});

addGlobalUncaughtErrorHandler((event) => {
    const { msg } = event;
    if (msg && msg.includes('died in status LOADING_SOURCE_CODE')) {
        console.log('加载失败');
    }
});

// start();

子应用main.js

import './public-path';
import Vue from 'vue'
import App from './App.vue'
import router from './router/index'
import store from './store/index'
import ViewUI from 'view-design';
import VueClipboard from 'vue-clipboard2';
import VueJsonp from 'vue-jsonp';
import debounce from './mixins/debounce';   // 全局防抖
import 'view-design/dist/styles/iview.css';
import installTrimDirective from '@/utils/trimDirective'
import * as _ from 'lodash'
import PerfectScrollbar from 'vue2-perfect-scrollbar'
import 'vue2-perfect-scrollbar/dist/vue2-perfect-scrollbar.css'
import menus from '@/store/modules/index/_types_thirdPartyStore'
Vue.prototype.$_ = _
window.sharedData = 'Hello from subapp';

Vue.use(PerfectScrollbar);
Vue.use(VueJsonp)
Vue.use(ViewUI);
Vue.use(VueClipboard)
Vue.mixin(debounce)
Vue.prototype.$bus = new Vue()
Vue.use(installTrimDirective) // * 全局去空格 v-trim

// 阻止启动生产消息
Vue.config.productionTip = false

let instance = null;
function render(props = {}) {
    instance = new Vue({
        el: '#app', // 这里应该替换为你实际的挂载点
        router,
        render: (h) => h(App),
        created() {
        }
    })
}

if (!window.__POWERED_BY_QIANKUN__) {
    render()
} else {

}

export async function bootstrap(props) {

}

export async function mount(props) {
    props.onGlobalStateChange((state) => {
        console.log('子应用接收的参数', state)
    }, true)
    props.setGlobalState({menus}) // 当子应用挂载完毕,子应用设置左侧路由数据给主应用

    if (!instance) {
        render();
    }
}

export async function unmount() {
    if (instance) {
        // 卸载 Vue 实例
        instance.$destroy();
        instance.$el.innerHTML = '';
        instance = null;
    }
}

注意:

  1. 工程中的token传递也可以通过initGlobalState时,传递给子应用,也可以通过挂载到localstorage上,由工程总体共用。
  2. 暂时未进行样式隔离等需求,待后续验证

原文地址:https://blog.csdn.net/Li_Ning21/article/details/140637918

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!