Single-spa流程分析

1,462 阅读4分钟

整体流程

初始化阶段

环境初始化

1、强化pushState和replaceState方法:由于调用history.pushState()或history.replaceState()不会触发popstate事件。所以需要改写。

2、收集和监听hashchange和popstate事件:触发应用加载,卸载等操作。

3、全局导出navigateToUrl:使用这个方法或自动判断应该刷新应用还是不刷新应用。

注册应用

1、处理应用参数的同时完善应用的相关参数,例如:初始化状态等

2、注册jQuery:使jQuery全局可用。

3、调用reroute。

校验阶段

运行阶段

调用start()之后:

加载阶段

1、加载应用资源更改状态

注:import()动态加载js,js又依赖其他js,这时候会等到全部加载完在执行下一步

启动阶段

1、在没有手动卸载资源或者刷新的前提下,只会调用一次声明周期:bootstrap

2、串行调用bootstrap的过程中,有时间调度检查。

挂载阶段

1、会多次触发生命周期:mount。

2、串行调用mount的过程中,有时间调度检查。

3、在没刷新的前提下,只会调用一次自定义事件:

  • single-spa:before-first-mount
  • single-spa:first-mount

卸载阶段

1、会多次触发生命周期:unmount。

2、串行调用mount的过程中,有时间调度检查。

应用整体生命周期调用场景

事件

1-1、single-spa:before-app-change:至少一个应用程序更改状态时触发。

window.addEventListener('single-spa:before-app-change', evt => {
  console.log('single-spa is about to mount/unmount applications!');
  console.log(evt.detail.originalEvent); // PopStateEvent
  console.log(evt.detail.newAppStatuses); // { app1: MOUNTED }
  console.log(evt.detail.appsByNewStatus); // { MOUNTED: ['app1'], NOT_MOUNTED: [] }
  console.log(evt.detail.totalAppChanges); // 1
});

1-2、single-spa:before-no-app-change:无应用程序更改状态时触发。

window.addEventListener('single-spa:before-no-app-change', evt => {
  console.log('single-spa is about to do a no-op reroute');
  console.log(evt.detail.originalEvent); // PopStateEvent
  console.log(evt.detail.newAppStatuses); // { }
  console.log(evt.detail.appsByNewStatus); // { MOUNTED: [], NOT_MOUNTED: [] }
  console.log(evt.detail.totalAppChanges); // 0
});

2、single-spa:before-routing-event:一个 single-spa:before-routing-event 事件在每个路由事件发生之前被触发,这是在每个 hashchange、popstate 或 triggerAppChange 之后,即使不需要对注册的应用程序进行更改。要取消导航事件,请监听 single-spa: before-routing-event 事件。

window.addEventListener(
  'single-spa:before-routing-event',
  ({ detail: { oldUrl, newUrl, cancelNavigation } }) => {
    if (
      new URL(oldUrl).pathname === '/route1' &&
      new URL(newUrl).pathname === '/route2'
    ) {
      cancelNavigation();
    }
  },
);

3、single-spa:before-mount-routing-event:它保证在卸载所有单spa 应用程序之后,但在安装任何新应用程序之前触发。

window.addEventListener('single-spa:before-mount-routing-event', evt => {
  console.log('single-spa is about to mount/unmount applications!');
  console.log(evt.detail);
  console.log(evt.detail.originalEvent); // PopStateEvent
  console.log(evt.detail.newAppStatuses); // { app1: MOUNTED }
  console.log(evt.detail.appsByNewStatus); // { MOUNTED: ['app1'], NOT_MOUNTED: [] }
  console.log(evt.detail.totalAppChanges); // 1
});

4、single-spa:before-first-mount:在安装第一个单spa 应用程序之前,单spa 会触发single-spa:before-first-mount 事件;因此它只会被发射一次。更具体地说,它在应用程序已经加载之后但在安装之前触发。推荐用法:在安装第一个应用程序之前,移除用户看到的加载条。

window.addEventListener('single-spa:before-first-mount', () => {
  console.log(
    'single-spa is about to mount the very first application for the first time',
  );
});

5、single-spa:first-mount:在装入任何单个spa应用程序中的第一个后,单个spa将触发此事件;因此,它只能调用一次。推荐用法:记录用户看到安装的任何应用之前所用的时间。

window.addEventListener('single-spa:first-mount', () => {
  console.log('single-spa just mounted the very first application');
});

6-1、single-spa:app-change:每次加载、引导、装载、卸载或卸载一个或多个应用时,都会触发一个spa:app change事件。它与single spa:routing事件类似,只是只有在一个或多个应用程序实际加载、引导、装载或卸载后才会触发。如果hashchange、popstate或triggerAppChange未导致这些更改之一,则不会触发事件。

window.addEventListener('single-spa:app-change', evt => {
  console.log(
    'A routing event occurred where at least one application was mounted/unmounted',
  );
  console.log(evt.detail.originalEvent); // PopStateEvent
  console.log(evt.detail.newAppStatuses); // { app1: MOUNTED, app2: NOT_MOUNTED }
  console.log(evt.detail.appsByNewStatus); // { MOUNTED: ['app1'], NOT_MOUNTED: ['app2'] }
  console.log(evt.detail.totalAppChanges); // 2
});

6-2、single-spa:no-app-change:当没有加载、引导、装载、卸载或卸载任何应用程序时,single spa触发一个spa:no app change事件。这与单个spa:app更改事件相反。每个路由事件只触发一个。

window.addEventListener('single-spa:no-app-change', evt => {
  console.log(
    'A routing event occurred where zero applications were mounted/unmounted',
  );
  console.log(evt.detail.originalEvent); // PopStateEvent
  console.log(evt.detail.newAppStatuses); // { }
  console.log(evt.detail.appsByNewStatus); // { MOUNTED: [], NOT_MOUNTED: [] }
  console.log(evt.detail.totalAppChanges); // 0
});

7、single-spa:routing-event:每次发生路由事件时,即在每次hashchange、popstate或triggerAppChange之后触发事件,即使不需要更改已注册的应用程序;在单一spa验证所有应用程序都已正确加载、引导、装载和卸载之后。

window.addEventListener('single-spa:routing-event', evt => {
  console.log('single-spa finished mounting/unmounting applications!');
  console.log(evt.detail.originalEvent); // PopStateEvent
  console.log(evt.detail.newAppStatuses); // { app1: MOUNTED, app2: NOT_MOUNTED }
  console.log(evt.detail.appsByNewStatus); // { MOUNTED: ['app1'], NOT_MOUNTED: ['app2'] }
  console.log(evt.detail.totalAppChanges); // 2
});

single-spa-react分析

bootstrap

根据参数,获取组件入口,同时可以获取到single-spa的参数。

if (opts.rootComponent) {
    // This is a class or stateless function component
    return Promise.resolve();
  } else {
    // They passed a promise that resolves with the react component. Wait for it to resolve before mounting
    return opts.loadRootComponent(props).then((resolvedComponent) => {
      opts.rootComponent = resolvedComponent;
    });
  }

mount

unmount

调用卸载代码

if (root && root.unmount) {
      // React >= 18
      const unmountResult = root.unmount();
    } else {
      // React < 18
      opts.ReactDOM.unmountComponentAtNode(opts.domElements[props.name]);
    }