SnapshotSandbox
2024年03月21日
一、认识
快照沙箱 SnapshotSandbox
顾名思义,即在某个阶段给当前的运行环境打一个快照,再在需要的时候把快照恢复,从而实现隔离。在创建微应用的时候会实例化一个沙盒对象,对于每一个子应用,运行时将其内部保存的上下文加载到对应的变量上,销毁时再将当前浏览器环境中各个变量的值保存到快照中。它有两个方法,active
是在激活微应用的时候执行,而 inactive
是在离开微应用的时候执行。
整体的思路是在激活微应用时将当前的 window
对象 拷贝存起来,然后从 modifyPropsMap
中恢复这个微应用上次修改的属性到 window
中。在离开微应用时会与原有的 window
对象 做对比,将有修改的属性保存起来,以便再次进入这个微应用时进行数据恢复,然后把有修改的属性值恢复到以前的状态。
二、特点
快照沙箱只能针对单实例应用场景,如果是多个实例同时挂载的情况则无法解决,这时只能通过Proxy代理
沙箱来实现。这个沙箱有个缺点,就是需要遍历window
上的所有属性,性能较差
三、实现
3.1 思路
变量如下:
-
deletePropsSet
: 记录被删除的属性 -
windowSnapshot
:windows
对象的快照,用于保存当前沙盒active
之前的window
对象的状态 -
modifyPropsMap
: 记录当前沙盒在active
和inactive
之间window
对象上修改过的值
逻辑如下:
-
active
方法: 快照激活,记录window
当时的状态,恢复上一次沙箱失活时记录的沙箱运行过程中对window
做的状态改变,也就是上一次沙箱激活后对window
做了哪些改变,现在也保持一样的改变。 -
inactive
方法: 快照失活,记录window
上有哪些状态发生了变化
3.2 实现
function iter(obj, callbackFn) {
for (const prop in obj) {
if (obj.hasOwnProperty(prop) || prop === "clearInterval") {
callbackFn(prop);
}
}
}
class SnapshotSandbox {
constructor(name) {
this.name = name;
this.modifyPropsMap = {};
this.windowSnapshot = {};
this.sandboxRunning = true;
this.type = "SnapshotSandbox";
this.deletePropsSet = new Set();
}
active() {
this.windowSnapshot = {};
iter(window, (prop) => {
this.windowSnapshot[prop] = window[prop];
});
Object.keys(this.modifyPropsMap).forEach((p) => {
window[p] = this.modifyPropsMap[p];
});
this.deletePropsSet.forEach((p) => {
delete window[p];
});
this.sandboxRunning = true;
}
inactive() {
this.modifyPropsMap = {};
this.deletePropsSet.clear();
iter(window, (prop) => {
if (this.windowSnapshot[prop] !== window[prop]) {
this.modifyPropsMap[prop] = window[prop];
window[prop] = this.windowSnapshot[prop];
}
});
iter(this.windowSnapshot, (prop) => {
if (!window.hasOwnProperty(prop)) {
this.deletePropsSet.add(prop);
window[prop] = this.windowSnapshot[prop];
}
});
this.sandboxRunning = false;
}
}
3.3 测试
// 激活应用时,记录 a 变量为 "哈哈"
snapshotSandBox.active();
window.a = "哈哈";
console.log(window.a);
// 应用失活时,尝试修改失活状态下的 a 变量, 正常来说失活状态下的 a 变量是不可修改的
snapshotSandBox.inactive();
window.a = "哈哈修改";
console.log(window.a);
// 激活应用时,测试 a 变量是否为 "哈哈"
snapshotSandBox.active();
console.log(window.a);