single-spa的源码解析

169 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

single-spa的官方文档

Single-spa 从现代框架组件生命周期中获得灵感,将生命周期应用于整个应用程序。

在启动阶段,就能看到生命周期的产生方式。

1. registerApplication: 注册阶段

1.1 registerApplication可以接受4个参数或者1个对象参数,

  • sanitize函数,用来对参数做校验,同时转化成对象的形式
{
    name"@single-spa/welcome"
    activeWhen: (loc: Location) => boolean
    customProps: {}
    loadApp: Function
}

  • 在全局,维护一个apps数组,用来保存所有app,每个app除了上述四个参数之外,还混入了下面其他状态
{
    loadErrorTime: null,
    status: NOT_LOADED,
    parcels: {},
    devtools: {
      overlays: {
        options: {},
        selectors: [],
      }
    }
}

1.2 reroute(pendingPromises)

  • getAppChanges: 首先根据当前appstatusappShouldBeActive(app参数中的activeWhen的状态)来将app分入四个状态池中:

image.png

  • 然后因为是在registryApp阶段,标识startfalse会走到loadApps函数中。

1.3 loadApps

  1. Promise.resolve()去异步执行了appsToLoadmap操作,而在map回调中调用了toLoadPromise(app)

        const loadPromises = appsToLoad.map(toLoadPromise)
    
  2. 执行完上述异步操作之后,去调用callAllEventListenerscallAllEventListeners内部是对去resolve pendingPromises,注入阶段的pendingPromises为空,所以这里就不做任务操作了

1.4. toLoadPromise

    1. 首先判断是否已加载:loadPromise是否存在,或者status是否是不加载(NOT_LOADED)或者是加载错误(Load_Error),存在的话直接返回loadPromise(从4下面的第4步可以看出该promise返回的是已加载的app)
    1. 如果以上都不是,将status 改为 LOADING_SOURCE_CODE
    1. 注入 loadPromisepromise,首先会异步调用 app.loadApp,而此时暴露在微应用中的bootstrapmount, unmount,unload会被加载进来,然后会对这些函数进行校验,校验成功之后将他们赋值给app中的对应的生命周期方法,同时将status修改为NOT_BOOTSTRAP,表明已加载,但是没有调用bootstrap。在赋值周期方法的时候,调用了flattenFnArray方法,他的作用是:bootstrapmount, unmount,unload都可以用数组的方式传入,flattenFnArray方法内部调用了Array.reduce方法来依次调用传入的生命周期数组中的方法。
    1. 如果加载过程中出现异常,status会变成SKIP_BECAUSE_BROKEN(传入的load不是返回promise)或者Load_ERROR,然后调用错误处理函数;如果正常加载完成,则返回app,此时app中的status已变成NOT_BOOTSTRAP,同时注入了bootstrap, mount... 等等生命周期方法

2. start: 启动阶段

2.1 置标志位start = true,表明已启动

2.2 第二次调用reroute函数

这次跟上一次有个区别在于,因为starttrue,这次会执行performAppChanges函数。

  • performAppChanges: 先卸载(unmount, unload),再装载(load, bootstrap, mount)

    • 异步触发自定义事件: a. single-spa:before-no-app-change或者single-spa:before-app-change, b. single-spa:before-routing-event, c. single-spa:before-mount-routing-event
    • unmounted unload app
    • 触发single-spa:before-mount-routing-event
    • mounted apps: 调用appsbootstrap钩子,成功之后调用mounted;如果需要load,先load
    • 触发single-spa:no-app-change或者 single-spa:app-change
    • 触发single-spa:routing-event
    • 返回mountedAppsname

3. renavigate阶段

当通过history或者hash进行路由变更的时候,会触发reroute函数,然后调用getAppChanges进行apps的状态收集,performAppChanges处理相应状态的apps

4. 写在结尾

这么看下来,single-spa库的源码逻辑还是比较清晰,也比较简单。下一周,源码分析基础该库封装的微应用框架qiankun

00 附录

  1. 自定义事件
// 监听
window.addEventListener('custom-event-name', (e) => {
    // e 自定义事件
    // 可以拿到detail等等参数
})

// 构建自定义事件
const customEvent = new Custom('custom-event-name', {
    isCancel: true, // 是否可取消
    bubbles: true, // 是否冒泡
    detail: {
       // 额外参数
    }
})

// 触发
dispatch(customEvent)