qiankun自动加载微应用实现和原理分析

547 阅读2分钟

背景

主应用: webpack + vue3 + vue-router

具体实现

  1. 在主应用根目录下新建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的前面要有/#

  1. 修改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()启动过程

未命名 4.jpg

start()中调用patchHistoryApi()

路径:single-spa/src/start.js

image.png

patchHistoryApi()中实现hashchange/popstate事件的监听,并重写window.history.pushStatewindow.history.replaceState

image.png

为什么要重写window.history.pushStatewindow.history.replaceState

window.history.pushStatewindow.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()

image.png

urlReroute()内调用reroute()image.png reroute()内部实现:

  1. 通过getAppChanges()拿到appsToUnload、appsToUnmount等。
  2. 如果是start为true,则执行performAppChanges()。

微应用挂载实现原理

在performAppChanges()内部遍历appsToMount数组,执行tryToBootstrapAndMount()。

image.png tryToBootstrapAndMount()内部首先调用toBootstrapPromise(),当当前打开的微应用全部卸载后,会再执行toMountPromise()image.png toBootstrapPromise()内部调用reasonableTime(),此时的生命周期是bootstrapimage.png 则后续后调用微应用导出的bootstrap生命周期钩子。

toMountPromise()内部也会调用reasonableTime(),但此时的生命周期是mount

image.png

微应用卸载实现原理

在performAppChanges()内部遍历appsToUnmount数组。

image.png

每次微应用mounted后,会将微应用实例push到appsToUnmount数组中

image.png

遍历appsToUnmount数组, 执行toUnmountPromise函数。

image.png toUnmountPromise()函数的第一个参数, 即是appsToUnmount数组的任意一项。执行unmountAppOrparcel()函数。

image.png 执行unmountAppOrparcel()函数内调用reasonableTime()函数,此时的生命周期是unmount

image.png 此时appOrParcel为待卸载的微应用, liftsycle为unmount,即执行微应用导出的unmount生命周期钩子。