子应用注册&路由拦截
子应用注册
qiankun注册子应用是通过single-spa API
registerApplication
完成的
- 路由匹配 activeRule 时调用
js
// 删除不必要代码
registerApps.forEach((app) => {
const { name, activeRule, props, ...appConfig } = app
registerApplication({
name,
activeWhen: activeRule, // 激活时机
customProps: props,
// 路由匹配 activeRule 时激活
app: async () => {
await loadApp({ name, props, ...appConfig }) // 加载子应用即其他处理
return {
// 这三个钩子都是建立在路由是否匹配上的场景
bootstrap: [...], // 首次执行(只执行一次)
mount: [...], // 首次执行(二次激活时也会执行,可能执行多次)
unmount: [...] // 卸载子应用时执行
}
}
})
})
注册流程
监听路由实现
- 通过劫持
history.pushState && history.replaceState
和 侦听window.onpopstate
事件触发应用是否加载
js
// 非single-spa源码
// 监听路由变化 决定是否加载子应用
export const rewriteRouter = () => {
// pushState && replaceState并没有响应的监听事件,所以需要手动添加劫持
window.history.pushState = patchRouter(window.history.pushState, 'micro_push')
window.history.replaceState = patchRouter(window.history.replaceState, 'micro_replace')
window.addEventListener('micro_push', turnApp) // 监听
window.addEventListener('micro_replace', turnApp) // 监听
// 监听返回、前进、history.go/back/forward事件
window.onpopstate = async function () {
await turnApp()
}
}
// patchRouter
const patchRouter = (rawFn, eventName) => {
return function () {
const e = new Event(eventName)
window.dispatchEvent(e) // 触发自定义事件,监听eventName的方法将被调用
rawFn.apply(this, arguments) // 调用原生方法
}
}
// turnApp
export const turnApp = async () => {
// 加载子应用
if (isTurnChild()) {
await // ...lifecycle() // 执行子应用对应的生命周期
}
}
// 子应用是否做了切换
export const isTurnChild = () => {
// eg: http://o.xx.com/app1/test#123
const { pathname, hash } = window.location
const url = pathname + hash
const currentPrefix = url.match(/(\/\w+)/g) // ['/app1', '/test']
// 根据第一个 path 来确定是否激活路由
if (
currentPrefix &&
(currentPrefix[0] === window.__CURRENT_SUB_APP__)
) {
return false; // 不匹配返回false
}
window.__ORIGIN_APP__ = window.__CURRENT_SUB_APP__;
const currentSubApp = window.location.pathname.match(/(\/\w+)/)
// 当前路由以改变,修改当前路由
window.__CURRENT_SUB_APP__ = currentSubApp[0];
return true;
}