微前端是一种多个团队
通过独立发布功能
的方式来共同构建
现代化 web 应用的技术手段及方法策略
- 技术栈无关
- 独立开发、独立部署
- 增量升级
- 独立运行时(微应用之间状态隔离、运行时状态不共享)
- 给 window 设置
__POWERED_BY_QIANKUN__
属性,在应用中用来判断是否是在主应用容器中运行 - 检查 prefetch 属性,如果需要预加载,则添加全局事件
single-spa:first-mount
监听,在第一个子应用挂载后加载其他子应用的资源,优化后续其他子应用加载的速度 - 根据 singluarMode 参数设置是否为单实例模式(单实例模式下,新的子应用的挂载必须是在旧的子应用卸载后)
- 根据 jsSandbox 参数设置是否启用沙箱运行环境
- 调用 single-spa 的 startSingleSpa 方法启动应用
- 已经注册过的子应用不需要再次注册,过滤掉已注册的内容
- 核心注册实现还是调用 single-spa 的 registerApplication 方法
- 设置子应用属性,name子应用名称,callback激活时的回调,activeRule激活规则,props传递给子应用的参数
- 获取子应用的 entry 和 name
- 根据子应用的 name 生成唯一的ID
- 获取子应用传过来的一些配置(是否开启沙箱等)
- 根据 entry 获取子应用的入口(可能是html,也可能是js)
- 获取子应用的渲染内容
- 处理子应用的样式(通过 shadow dom 实现样式隔离)
- 设置子应用的渲染容器
- 获取子应用的 render 函数,执行 render 函数
- 通过 import-html-entry 库,根据 entry 进入加载之应用
- 返回一个对象,包含:将脚本内容注释后的模板,资源地址根路径,获取外部引入的脚本文件的方法,获取外部引入的样式表文件的方法,执行模板文件中所有 JS 脚本文件的方法(可以指定脚本的作用域)
- 首先会判断浏览器是否支持 Proxy
- 如果支持 Proxy 使用的是 LegacySandbox 或 ProxySandbox,区别是 LegacySandbox 是单实例,ProxySandbox 是多实例
- 如果不支持 Proxy 使用的快照沙箱
LegacySandbox 本质是利用 Proxy,在子应用中运行过程中,通过 两个 map 对象来分别记录新增的全局变量和修改的全局变量(addedPropsMapInSandbox,modifiedPropsOriginalValueMapInSandbox),还有一个 map 对象来记录所有操作过的全局对象(currentUpdatedPropsValueMap)。 在沙箱关闭时,通过 addedPropsMapInSandbox 删除子应用运行时新增的所有全局变量,通过 modifiedPropsOriginalValueMapInSandbox 还原子应用运行时修改过的全局变量
总结:LegacySandbox 是利用快照实现沙箱隔离的(首先背会,沙箱关闭时还原到之前的状态)
LegacySandbox 本质也是利用 Proxy,但是会把子应用运行时的所有对全局的设置代理到 updateValueMap 上,对于全局对象的取值会优先从 updateValueMap 上取,如果没有则从 window 上取,如果取的是非构造函数之外的函数,返回的函数是通过bind绑定this到window的函数
总结:ProxySandbox 是代理沙箱
genSandbox 函数不仅返回了一个 sandbox 沙箱,还返回了 mount 和 unmount 方法,分别在子应用挂载时和卸载时调用
mount 方法首先调用沙箱的激活方法,然后劫持了计时器,window事件监听,window.history事件监听,动态添加 head 元素事件
在子应用运行时,会开启定时器,在子应用卸载的时候,应该将这些定时器销毁,同样的 事件和 history 事件的处理与定时器类似;
首先对挂载沙箱时,定义的计数器,绑定的事件的移除 调用沙箱的卸载方法
- Actions 通信
Actions 通信,本质是观察者模式,在主应用中通过 initGlobalState 定义好全局状态,同时可以通过 action.onGlobalStateChange 注册观察者,同样的子应用在 mount 生命周期中可以通过 props.onGlobalStateChange 注册观察者,然后修改状态是通过 setGlobalState 来修改状态,状态修改后,注册的所有观察者都可以拿到变更前的状态和变更后的状态
缺点:
- 子应用独立运行时,需要额外的配置无 Actions 的逻辑
- 子应用需要先了解状态池的细节,在进行通信
- 由于状态池无法跟踪,通信场景较多时,容易出现状态混乱,维护困难等问题
- Shared 通信
Shared 通信的原理是,主应用基于 redux 维护一个状态池,通过 Shared 实例暴露一些方法给子应用使用(比如获取状态、设置状态)。同时,子应用需要单独维护一份 shared 实例,在独立运行时,使用自己的 shared 实例,在嵌入子应用时使用主应用的 shared 的实例。
子应用是如何判断使用自己的 shared 实例还是使用主应用的 shared 实例呢?
子应用的 Shared 类中,定义一个重载 shared 实例的方法,当主应用有传递 shared 实例的时候,就用主应用的 shared 实例重载自己的 shared 实例。