Skip to content

子应用注册&路由拦截

子应用注册

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: [...] // 卸载子应用时执行
      }
    }
  })
})

注册流程

image

监听路由实现

  • 通过劫持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;
}