文章目录
- JS隔离
- 【降级处理】SnapshotSandbox快照沙箱
- 代理沙箱:legacySandbox单例沙箱
- 【qiankun主要方式】代理沙箱:proxySandbox多例沙箱
- CSS隔离
- Shadow DOM
- CSS模块(CSS Modules)
- 样式表的装载和卸载
- 样式隔离踩坑记
JS隔离
JS 沙箱简单点说就是,主应用有一套全局环境 window,子应用有一套私有的全局环境 fakeWindow,子应用所有操作都只在新的全局上下文中生效,这样的子应用好比被一个个箱子装起来与主应用隔离,因此主应用加载子应用便不会造成 JS 变量的相互污染、JS 副作用、CSS 样式被覆盖等,每个子应用的全局上下文都是独立的。
【降级处理】SnapshotSandbox快照沙箱
快照沙箱就是在应用沙箱挂载和卸载的时候记录快照,在应用切换的时候依据快照恢复环境。
缺点,无法同时有多个运行时快照沙箱,否则在 window 上修改的记录会混乱,一个页面只能运行一个单实例微应用
基于diff来实现的,主要用于不支持window.Proxy的低版本浏览器,而且也只适应单个的子应用
原理: 激活沙箱时,将window的快照信息存到windowSnapshot中, 如果modifyPropsMap有值,还需要还原上次的状态;激活期间,可能修改了window的数据;退出沙箱时,将修改过的信息存到modifyPropsMap里面,并且把window还原成初始进入的状态。
- 微应用 mount 时
先把上一次记录的变更 modifyPropsMap 应用到微应用的全局 window,没有则跳过
浅复制主应用的 window key-value 快照,用于下次恢复全局环境 - 微应用 unmount 时
将当前微应用 window 的 key-value 和 快照 的 key-value 进行 Diff,Diff 出来的结果用于下次恢复微应用环境的依据
将上次快照的 key-value 拷贝到主应用的 window 上,以此恢复环境
优缺点分析: snapshotSandbox会污染全局window,但是可以支持不兼容Proxy的浏览器
代理沙箱:legacySandbox单例沙箱
当有多个实例的时候,比如有 A、B 两个应用,A 应用就活在 A 应用的沙箱里面,B 应用就活在 B 应用的沙箱里面,A 和 B 无法互相干扰,这样的沙箱就是代理沙箱,这个沙箱的实现思路其实也是通过 ES6 的 proxy代理特性实现的。
缺点:在全局作用域上通过 var 或 function 声明的变量和函数无法被代理沙箱劫持,因为代理对象 Proxy 只能识别在该对象上存在的属性,通过 var 或 function 声明声明的变量是开辟了新的地址,自然无法被 Proxy 劫持
基于es6的Proxy实现
原理:legacySandbox设置了三个参数来记录全局变量,分别是记录沙箱新增的全局变量addedPropsMapInSandbox、记录沙箱更新的全局变量modifiedPropsOriginalValueMapInSandbox、持续记录更新的(新增和修改的)全局变量,用于在任意时刻做snapshot的currentUpdatedPropsValueMap。
通过监听对 window 的修改来直接记录 Diff 内容,因为只要对 window 属性进行设置,那么就会有两种情况:
- 如果是新增属性,那么存到 addedMap 里
- 如果是更新属性,那么把原来的键值存到 prevMap,把新的键值存到 newMap
通过 addedMap, prevMap 和 newMap 这三个变量就能反推出微应用以及原来环境的变化,qiankun 也能以此作为恢复环境的依据。
【qiankun主要方式】代理沙箱:proxySandbox多例沙箱
原理:激活沙箱后,每次对window取值的时候,先从自己沙箱环境的fakeWindow里面找,如果不存在,就从rawWindow(外部的window)里去找;当对沙箱内部的window对象赋值的时候,会直接操作fakeWindow,而不会影响到rawWindow。
- 把当前 window 的一些原生属性(如document, location等)拷贝出来,单独放在一个对象上,这个对象也称为 fakeWindow
- 之后对每个微应用分配一个 fakeWindow
- 当微应用修改全局变量时:
如果是原生属性,则修改全局的 window
如果不是原生属性,则修改 fakeWindow 里的内容 - 微应用获取全局变量时:
如果是原生属性,则从 window 里拿
如果不是原生属性,则优先从 fakeWindow 里获取
这样一来连恢复环境都不需要了,因为每个微应用都有自己一个环境,当在 active 时就给这个微应用分配一个 fakeWindow,当 inactive 时就把这个 fakeWindow 存起来,以便之后再利用。
优缺点分析:不会污染全局window,支持多个子应用同时加载。
CSS隔离
Shadow DOM
qiankun主要通过使用Shadow DOM来实现CSS隔离。
Shadow DOM:Shadow DOM是一种浏览器内置的Web标准技术,它可以创建一个封闭的DOM结构,这个DOM结构对外部是隔离的,包括其CSS样式。qiankun在挂载子应用时,会将子应用的HTML元素挂载到Shadow DOM上,从而实现CSS的隔离
// qiankun使用Shadow DOM挂载子应用
const container = document.getElementById('container');
const shadowRoot = container.attachShadow({mode: 'open'});
shadowRoot.innerHTML = '<div id="subapp-container"></div>';
潜在的缺点是它需要浏览器支持Shadow DOM,这在一些旧的浏览器或者不兼容Shadow DOM的浏览器中可能会出现问题。
CSS模块(CSS Modules)
CSS模块是一种将CSS类名局部化的方式,可以避免全局样式冲突。在使用CSS模块时,每个模块的类名都会被转换成一个唯一的名字,从而实现样式的隔离。qiankun 可以帮助自动化这个过程,但是它需要子应用在构建时合作,即在构建过程中将所有的类名前加上特定的字符串。
样式表的装载和卸载
当切换到一个子应用时,qiankun 动态加载这个子应用的样式文件,并在切换离开这个子应用时卸载它的样式文件。这样可以确保只有当前活动的子应用的样式会被应用。
样式隔离踩坑记
elementUI组件的一些弹窗默认是挂载到 body 上的,这就导致了再使用微前端集成的时候,子应用的弹窗逃逸,导致无法控制其弹窗的样式。
解决办法:先给组件全局配置一个 content 属性,接受需要挂载容器的 ID
然后利用计算属性,获取全局挂载容器,然后把默认的 document.body 替换成全局的挂载容器就可以简单的实现。