跳到主要内容

SnapshotSandbox

2024年03月21日
柏拉文
越努力,越幸运

一、认识


快照沙箱 SnapshotSandbox 顾名思义,即在某个阶段给当前的运行环境打一个快照,再在需要的时候把快照恢复,从而实现隔离。在创建微应用的时候会实例化一个沙盒对象,对于每一个子应用,运行时将其内部保存的上下文加载到对应的变量上,销毁时再将当前浏览器环境中各个变量的值保存到快照中。它有两个方法,active 是在激活微应用的时候执行,而 inactive 是在离开微应用的时候执行。

整体的思路是在激活微应用时将当前的 window 对象 拷贝存起来,然后从 modifyPropsMap 中恢复这个微应用上次修改的属性到 window 中。在离开微应用时会与原有的 window 对象 做对比,将有修改的属性保存起来,以便再次进入这个微应用时进行数据恢复,然后把有修改的属性值恢复到以前的状态。

二、特点


快照沙箱只能针对单实例应用场景,如果是多个实例同时挂载的情况则无法解决,这时只能通过Proxy代理沙箱来实现。这个沙箱有个缺点,就是需要遍历window上的所有属性,性能较差

三、实现


3.1 思路

变量如下:

  • deletePropsSet: 记录被删除的属性

  • windowSnapshot: windows对象的快照,用于保存当前沙盒active之前的window对象的状态

  • modifyPropsMap: 记录当前沙盒在 activeinactive 之间 window 对象上修改过的值

逻辑如下:

  1. active 方法: 快照激活,记录window当时的状态,恢复上一次沙箱失活时记录的沙箱运行过程中对window做的状态改变,也就是上一次沙箱激活后对window做了哪些改变,现在也保持一样的改变。

  2. 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);

参考资料


微前端框架 Qiankun 沙箱原理

【微前端】在造一个微前端轮子之前,你需要知道这些~