为什么要隔离环境
假如我们在其中一个子应用中设置全局变量
window.a = 1
如果我们没有做隔离环境,当我们切换到另一个子应用时,也可以获取到这个全局变量
- 对于这样的情况,如果是公共的依赖变量是没有问题的,
- 但是如果我们这个变量只在当前子应用应用,切换到另一个子应用时不需要这个变量。
- 由于我们可以获取到,多多少少对我们变量管理会有些影响
- 所以,我们当前子应用设置的变量只在当前使用,切换之后就销毁,如果有需要全局使用的公共变量,我们可以设置在主应用中,通过主应用来获取
快照沙箱
快照沙箱封装:main/micro/sandbox/snapshotSandBox.js
// 快照沙箱// 针对给当前全局变量实现快照方式,来记录沙箱内容,子应用切换后,全局变量恢复初始值// 应用场景:比较老版本的浏览器export class SnapShotSandBox {constructor() {// 1. 代理对象this.proxy = window;this.active();}// 沙箱激活active() {this.snapshot = new Map(); // 创建 window 对象的沙箱快照// 遍历全局环境for (const key in window) {// eslint-disable-next-line no-prototype-builtinsif (window.hasOwnProperty(key)) {// 将window上的属性进行拍照this.snapshot[key] = window[key];}}console.log(this.snapshot);}// 沙箱销毁inactive() {console.log(this.snapshot);for (const key in window) {// eslint-disable-next-line no-prototype-builtinsif (window.hasOwnProperty(key)) {// 将上次快照的结果和本次window属性做对比if (key == 'a') {console.log('对比', key, '----', window[key], '----', this.snapshot[key]);}if (window[key] !== this.snapshot[key]) {// 还原windowwindow[key] = this.snapshot[key];}}}}}
执行子应用js脚本前,创建沙箱环境main/micro/sandbox/index.js
export const sandbox = (app, script) => {// 创建沙箱环境const proxy = new SnapShotSandBox();// 判断当前应用有没有沙箱if (!app.proxy) {app.proxy = proxy;}...}
因为我们会加载多个子应用的js脚本,我们可以通过输出,可以看出,第一次的沙箱环境是纯环境的(没有自定义变量)
- 加载子应用第一个js脚本

- 加载子应用最后一个js脚本

所以我们把第一个快照存储到子应用上,后面销毁的时候做对比,就不会带着当前子应用设置的变量了。
切换子应用时,销毁上一个子应用以及对应的沙箱main/micro/lifeCycle.js/index.js
// 有上一个子应用,销毁子应用if (prevApp && prevApp.unmount) {if (prevApp.proxy) {prevApp.proxy.inactive() // 将沙箱销毁}await destoryed(prevApp)}
代理沙箱
使用ES6中的Proxy可以更好地实现代理机制 ```javascript export const isFunction = (value) => typeof value === ‘function’;
let defaultValue = {}; // 代理沙箱 export class ProxySandBox { constructor() { this.proxy = null; this.active(); } active() { // 当我们操作window对象时,操作的是代理对象 // 设置的值放在defaultValue中 this.proxy = new Proxy(window, { get(target, propKey) { // console.log(‘propKey’, propKey); // 函数做特殊处理,this指向原对象 if (isFunction(target[propKey])) { return target[propKey].bind(target) } // 如果代理值查找不到就使用原值 return defaultValue[propKey] || target[propKey]; }, set(target, propKey, value) { defaultValue[propKey] = value return true } }); } inactive() { defaultValue = {}; console.log(‘关闭沙箱’); } } ```
- 之前我们做的快照沙箱,在主应用中可以获取到子应用的全局变量
- 使用代理沙箱后,子应用的全局变量只能在子应用中使用。
