一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。
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: 首先根据当前app的status和appShouldBeActive(app参数中的activeWhen的状态)来将app分入四个状态池中:
- 然后因为是在
registryApp阶段,标识start为false会走到loadApps函数中。
1.3 loadApps
-
用
Promise.resolve()去异步执行了appsToLoad的map操作,而在map回调中调用了toLoadPromise(app)const loadPromises = appsToLoad.map(toLoadPromise) -
执行完上述异步操作之后,去调用
callAllEventListeners,callAllEventListeners内部是对去resolvependingPromises,注入阶段的pendingPromises为空,所以这里就不做任务操作了
1.4. toLoadPromise
-
- 首先判断是否已加载:
loadPromise是否存在,或者status是否是不加载(NOT_LOADED)或者是加载错误(Load_Error),存在的话直接返回loadPromise(从4下面的第4步可以看出该promise返回的是已加载的app)
- 首先判断是否已加载:
-
- 如果以上都不是,将
status改为LOADING_SOURCE_CODE
- 如果以上都不是,将
-
- 注入
loadPromise为promise,首先会异步调用app.loadApp,而此时暴露在微应用中的bootstrap,mount,unmount,unload会被加载进来,然后会对这些函数进行校验,校验成功之后将他们赋值给app中的对应的生命周期方法,同时将status修改为NOT_BOOTSTRAP,表明已加载,但是没有调用bootstrap。在赋值周期方法的时候,调用了flattenFnArray方法,他的作用是:bootstrap,mount,unmount,unload都可以用数组的方式传入,flattenFnArray方法内部调用了Array.reduce方法来依次调用传入的生命周期数组中的方法。
- 注入
-
-
如果加载过程中出现异常,
status会变成SKIP_BECAUSE_BROKEN(传入的load不是返回promise)或者Load_ERROR,然后调用错误处理函数;如果正常加载完成,则返回app,此时app中的status已变成NOT_BOOTSTRAP,同时注入了bootstrap,mount... 等等生命周期方法
-
2. start: 启动阶段
2.1 置标志位start = true,表明已启动
2.2 第二次调用reroute函数
这次跟上一次有个区别在于,因为start为true,这次会执行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: 调用apps的bootstrap钩子,成功之后调用mounted;如果需要load,先load- 触发
single-spa:no-app-change或者single-spa:app-change - 触发
single-spa:routing-event - 返回
mountedApps的name
- 异步触发自定义事件: a.
3. renavigate阶段
当通过history或者hash进行路由变更的时候,会触发reroute函数,然后调用getAppChanges进行apps的状态收集,performAppChanges处理相应状态的apps。
4. 写在结尾
这么看下来,single-spa库的源码逻辑还是比较清晰,也比较简单。下一周,源码分析基础该库封装的微应用框架qiankun。
00 附录
// 监听
window.addEventListener('custom-event-name', (e) => {
// e 自定义事件
// 可以拿到detail等等参数
})
// 构建自定义事件
const customEvent = new Custom('custom-event-name', {
isCancel: true, // 是否可取消
bubbles: true, // 是否冒泡
detail: {
// 额外参数
}
})
// 触发
dispatch(customEvent)