实际公司项目的微前端实现
更新时间: 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
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
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
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
2
3
4
5
6
7
现在public子系统中的组件就变成公共组件了。。其实这种方法相当绕。。如果用的react的话是不需要这么麻烦的。。没办法。。先这么用着