背景
主应用: webpack + vue3 + vue-router
具体实现
- 在主应用根目录下新建qiankun.ts文件, 文件内容如下:
import {
registerMicroApps,
start,
addGlobalUncaughtErrorHandler,
} from "qiankun"
console.log("注册微应用")
registerMicroApps([
{
name: "vue3-element-admin",
entry: "https://www.ysg.com:3001/#/home",
container: "#contentContainer",
activeRule: "/#/vue3-element-admin",
},
])
addGlobalUncaughtErrorHandler((event: Event | string) => {
console.error(event)
const { message } = event as any
console.log(message)
})
start()
⚠️注意:主应用vue-router是hash模式,所以activeRule的前面要有/#
- 修改App.vue根组件
修改之前:
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
修改之后:
<router-view v-slot="{ Component }" v-show="$route.name">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
// 增加一个挂载微应用的容器节点
<div v-show="!$route.name" id="qiankun_container"></div>
如此便可, 不需要配置路由表,不需要新增新的组件。
qiankun的start()启动过程
start()中调用patchHistoryApi()
路径:single-spa/src/start.js
patchHistoryApi()中实现hashchange/popstate事件的监听,并重写window.history.pushState和window.history.replaceState。
为什么要重写window.history.pushState和window.history.replaceState?
window.history.pushState和window.history.replaceState不会触发popstate事件,
所以这里有重写这两个API。下面是具体实现:
window.history.pushState = patchedUpdateState(
window.history.pushState,
"pushState"
);
window.history.replaceState = patchedUpdateState(
originalReplaceState,
"replaceState"
);
function patchedUpdateState(updateState, methodName) {
return function () {
const urlBefore = window.location.href;
const result = updateState.apply(this, arguments);
const urlAfter = window.location.href;
if (!urlRerouteOnly || urlBefore !== urlAfter) {
// fire an artificial popstate event so that
// single-spa applications know about routing that
// occurs in a different application
// dispatchEvent() 方法会向一个指定的事件目标派发一个事件
window.dispatchEvent(
createPopStateEvent(window.history.state, methodName)
);
}
return result;
};
}
function createPopStateEvent(state, originalMethodName) {
// https://github.com/single-spa/single-spa/issues/224 and https://github.com/single-spa/single-spa-angular/issues/49
// We need a popstate event even though the browser doesn't do one by default when you call replaceState, so that
// all the applications can reroute. We explicitly identify this extraneous event by setting singleSpa=true and
// singleSpaTrigger=<pushState|replaceState> on the event instance.
let evt;
try {
evt = new PopStateEvent("popstate", { state });
} catch (err) {
// IE 11 compatibility https://github.com/single-spa/single-spa/issues/299
// https://docs.microsoft.com/en-us/openspecs/ie_standards/ms-html5e/bd560f47-b349-4d2c-baa8-f1560fb489dd
evt = document.createEvent("PopStateEvent");
evt.initPopStateEvent("popstate", false, false, state);
}
evt.singleSpa = true;
evt.singleSpaTrigger = originalMethodName;
return evt;
}
hashchange/popstate事件的回调函数是urlReroute()
urlReroute()内调用reroute()。
reroute()内部实现:
- 通过getAppChanges()拿到appsToUnload、appsToUnmount等。
- 如果是start为true,则执行performAppChanges()。
微应用挂载实现原理
在performAppChanges()内部遍历appsToMount数组,执行tryToBootstrapAndMount()。
tryToBootstrapAndMount()内部首先调用
toBootstrapPromise(),当当前打开的微应用全部卸载后,会再执行toMountPromise()。
toBootstrapPromise()内部调用reasonableTime(),此时的生命周期是
bootstrap。
则后续后调用微应用导出的bootstrap生命周期钩子。
toMountPromise()内部也会调用reasonableTime(),但此时的生命周期是mount。
微应用卸载实现原理
在performAppChanges()内部遍历appsToUnmount数组。
每次微应用mounted后,会将微应用实例push到appsToUnmount数组中
遍历appsToUnmount数组, 执行toUnmountPromise函数。
toUnmountPromise()函数的第一个参数, 即是appsToUnmount数组的任意一项。执行unmountAppOrparcel()函数。
执行unmountAppOrparcel()函数内调用reasonableTime()函数,此时的生命周期是
unmount。
此时appOrParcel为待卸载的微应用, liftsycle为unmount,即执行微应用导出的unmount生命周期钩子。