实际公司项目的微前端实现

更新时间: 2021-11-29 10:34:49

原本我是不想写这个的,因为有前面四篇就够了,但是!生活永远比想象中要精彩啊!!公司项目里骚操作简直了。。。怕我自己忘了我还是记一下吧。

是这样,公司项目呢我原本划分成了一个主系统和三个子系统,本来这三个子系统互不打扰,主系统也足够精简。。。但是呢,来着这样一个需求,某个页面需要同时出现在两个子系统中,这样一来,这个页面放在主系统吧,会造成主系统臃肿,放在子系统吧,会造成代码有两份,到时候改起来超麻烦。。于是,诞生了一种骚操作:

新开发一个子系统,将所有公共的页面组件都放在这个子系统中,然后在主系统里将这个子系统的组件全部注册成为公共组件

很迷茫是不是。。我也是。。。

# 公共子系统

首先还是建立一个公共子系统public,这个子系统包含所有公共组件,并且要做以下事情:

  • 在子系统加载时将所有要注册为全局组件的都挂载到props.window下,虽然现在没有这个props.window,但是后面注册这个子系统时会加上的

首先是子系统的main.js:










 




































 
 












































import Vue from 'vue'
import Vuex from 'vuex'
import actions from "@/shared/actions";
import "./public-path";
import App from './App.vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import store from './store'
import "./icons"
// publicComponents导出的是要挂载的组件集合
import publicComponents from '@/components/publicComponents.js'
import { util } from '@/utils/common.js'

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

Vue.config.productionTip = false
Vue.use(ElementUI);
Vue.use(Vuex);

let instance = null;

/**
 * 渲染函数
 * 两种情况:主应用生命周期钩子中运行 / 微应用单独启动时运行
 */
function render(props) {
  if (props) {
    for(let plugin in props.plugin){
      //注册插件
      Vue.use(props.plugin[plugin]);
    }
    //注册全局组件
    for(let component in props.components){
      Vue.component(component, props.utils.contentExtend(props.components[component]))
    }
    //所有公共方法都被添加到了common.js的util中
    for(let fuc in props.utils){
      util[fuc] = props.utils[fuc]
    }
    Vue.prototype.$Bus = props.Bus;
    Vue.prototype.$ThemeInfo = props.ThemeInfo;
    //封装的axios方法也拿过来了
    for(let key in props.requestMethods){
      Vue.prototype[key] = props.requestMethods[key]
    }
    //;
    
    //将组件都挂载到props.window上
    props.window.publicComponents = publicComponents


    // 注入 actions 实例,虽然主应用把actions的一些方法放进了util,但是还是保留子应用自主使用actions的能力
    actions.setActions(props);
  }

  // 挂载应用
  instance = new Vue({
    store,
    render: (h) => h(App),
  }).$mount("#public");
}

// 独立运行时,直接挂载应用
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

/**
 * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
 * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
 */
export async function bootstrap() {
  console.log("VuePublicApp bootstraped");
}

/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
export async function mount(props) {
  console.log("VuePublicApp mount", props);
  render(props);
}

/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount() {
  console.log("VueMicroApp unmount");
  instance.$destroy();
  instance = null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

# public子系统注册

然后在主系统中注册这个子系统,这个子系统和其他子系统不一样,其他子系统需要以它的组件为依赖,所以在public子系统没有加载完的情况下,我用一个变量不让其他子系统页面加载出来,不然会报错。 public子系统使用loadMicroApp方法。


















































 
 
 
 
 
 
 
 
 

// apps.js 主系统中的子系统配置
import vMessage from '@/components/Message/index.js' 
import {directiveInstall} from "@/directives/index.js"
import publicMethod from '@/utils/publicMethod.js'
import requestMethods from "@/utils/request"
import publicComponents from '@/components/publicComponents.js'
import ThemeInfo from '@/style/theme/themeInfo'
import Bus from '@/utils/bus'
import mapTheme from '@/data/mapTheme'
import i18n from '@/utils/i18n'
//此时我们还没有微应用,所以app为空
let props = {
  requestMethods:requestMethods,//将封装的axios的方法传递给子系统
  plugin:{
    directiveInstall,//公用指令注册插件
    vMessage,//将这个插件传给子应用
  },
  Bus,
  i18n:i18n,
  mapTheme,
  ThemeInfo :ThemeInfo,
  components:publicComponents, 
  utils:publicMethod, //传递公用方法
}
let link = location.protocol+"//"+location.hostname
let activeRuleLink = location.pathname
export const apps = [
  {
    name: "platform",
    entry: link+":185",
    container: "#frame",
    activeRule: activeRuleLink+"#/platform/",
    props:props
  },
  {
    name: "customized",
    entry: link+":183",
    container: "#frame",
    activeRule: activeRuleLink+"#/customized/",
    props:props
  },
  {
    name: "analysis",
    entry: link+":184",
    container: "#frame",
    activeRule: activeRuleLink+"#/analysis/",
    props:props
  }
]
export const comp = {
  name: "publicComponents",
  entry: link+":186",
  container: "#publicComponents",
  props: {
    //这个地方传递window
    ...{window:window},...props
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

然后在App.vue的mounted钩子中加载public子系统:

<template>
  <div class="main-contenter">
    <layout v-if="publicAppMounted"></layout>
    <div id="publicComponents"></div>
  </div>
</template>

<script>
import {sendGlobalInfo} from "@/utils/actions";
import { mapGetters,mapActions } from 'vuex'
import {
  loadMicroApp
} from "qiankun"
import {comp} from "@/micro/apps";
import {contentExtend} from "@/utils/common";
import Vue from 'vue'

export default {
  name: 'App',
  components:{
    layout: () => import("@/layout/index.vue")
  },
  data(){
        return {
          publicApp:null,
          publicAppMounted:false
        }
    },
    mounted() {
        //...
        this.publicApp = loadMicroApp(comp)
        this.publicApp.mountPromise.then(() => {
            for(let component in window.publicComponents){
                Vue.component(component, contentExtend(window.publicComponents[component]))
            }
            this.publicAppMounted = true
        })

        //两秒过后如果publicApp加载失败也把layout放开
        setTimeout(() => {
            if(!this.publicAppMounted){
                this.publicAppMounted = true
            }
        },2000)
    },
    methods:{
        logoutUser(){
            this.LogOut()
        },
        ...mapActions({
            LogOut: 'LogOut'
        })
    }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// common.js
// ...
export function contentExtend(obj){
	function F(){}
	F.prototype = obj
	return new F();
}
1
2
3
4
5
6
7

现在public子系统中的组件就变成公共组件了。。其实这种方法相当绕。。如果用的react的话是不需要这么麻烦的。。没办法。。先这么用着