为什么要隔离环境
假如我们在其中一个子应用中设置全局变量
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-builtins
if (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-builtins
if (window.hasOwnProperty(key)) {
// 将上次快照的结果和本次window属性做对比
if (key == 'a') {
console.log('对比', key, '----', window[key], '----', this.snapshot[key]);
}
if (window[key] !== this.snapshot[key]) {
// 还原window
window[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(‘关闭沙箱’); } } ```
- 之前我们做的快照沙箱,在主应用中可以获取到子应用的全局变量
- 使用代理沙箱后,子应用的全局变量只能在子应用中使用。