今天主要介绍快照沙箱,直接上核心代码。
class SnapshotSandBox {
windowSnapshot = {};
modifyPropsMap = {};
active() {
for (const prop in window) {
this.windowSnapshot[prop] = window[prop];
}
Object.keys(this.modifyPropsMap).forEach(prop => {
window[prop] = this.modifyPropsMap[prop];
});
}
inactive() {
for (const prop in window) {
if (window[prop] !== this.windowSnapshot[prop]) {
this.modifyPropsMap[prop] = window[prop];
window[prop] = this.windowSnapshot[prop];
}
}
}
}
// 验证:
let snapshotSandBox = new SnapshotSandBox();
snapshotSandBox.active();
window.city = 'Jinan';
console.log("window.city-01:", window.city); // result Jinan
snapshotSandBox.inactive();
console.log("window.city-02:", window.city); // result undefined
snapshotSandBox.active();
console.log("window.city-03:", window.city); // result Jinan
代码分析
class说明
1.定义了两个空的对象
windowSnapshot和modifyPropsMap;
2.定义了active方法,该方法先是遍历的window对象上的所有属性,分别将其拷贝至对应的windowSnapshot上;然后迭代了modifyPropsMap对象上的属性,将各个属性拷贝给window对象对应的属性上。
3.定义了inactive方法,该方法遍历window对象上的所有属性,如果window对象上的属性的值不等于对应的windowSnapshot对象上的值,就将window对象属性的值拷贝给对应modifyPropsMap对象属性的值,并且将window对象的属性的值恢复至windowSnapshot对象所存储的对应的值;
调用说明
1.实例化类SnapshotSandBox
2.调用“snapshotSandBox”对象的“active()”方法。 这会拍摄“window”对象当前状态的快照,并根据“modifyPropsMap”对象中的值修改其属性(当前为空数组[],所以不会走forEach回调) 3.在“window”对象上设置一个新属性“city”,其值为“Jinan”。
4.将“window.city”的值打印到控制台,该值将是“Jinan”,因为它刚刚在上一步中设置。
5.调用“snapshotSandBox”对象的“inactive()”方法。 这将检查自拍摄快照以来“window”对象的任何属性是否已被修改。 在这种情况下,“city”属性已被修改,将当前修改的值存入modifyPropsMap对象,并且将window对象上对应属性的值恢复为“undefined”。
6.将“window.city”的值记录到控制台,该值将是“undefined”,因为它在上一步中已恢复。
7.再次调用“snapshotSandBox”对象的“active()”方法。 这会获取“window”对象当前状态的新快照,并根据“modifyPropsMap”对象中的值修改其属性。(因为之前在inactive方法中保存了修改的值)
8.将“window.city”的值记录到控制台,该值将是“Jinan”,因为它刚刚在上一步中设置。
总结:
沙箱激活时,保存当前window对象的快照,并将上一次改变的状态恢复至window对象上。
沙箱失活时,将当前对window对象的改变保存起来,并将window对象恢复至之前window快照时的状态。
如果同时运行多个微应用,都对window对象进行修改,会出现状态混乱的情况,所以快照沙箱只支持对单个微应用的运行,而且通过对window对象所有属性进行diff,是一件比较消耗性能的事情,后面会介绍通过es6的proxy的方式实现的其他隔离方式以及支持多个微应用同时运行。