一、什么是沙箱
Qiankun微服务框架的沙箱机制是其核心功能之一,主要用于隔离不同子应用之间的运行环境,防止全局污染,保证各个子应用能够独立、安全地运行,沙箱的实现主要有两类:proxy沙箱及快照沙箱
二、proxy沙箱
原理概述:
Proxy对象的使用:Qiankun使用JavaScript的Proxy对象来创建一个全局对象的代理。通过这个代理,可以拦截并控制子应用对全局对象的访问和修改。
拦截与重定向:当子应用尝试读取或修改全局对象(如window对象)的属性时,Proxy会拦截这些操作,并根据需要进行重定向。读取操作时,如果属性存在于代理对象中,则直接返回该值;否则,从全局对象中读取并返回。修改操作时,将修改应用到代理对象上,而不是全局对象上。
具体实现:
- 创建一个包含所有全局变量和函数的“fakeWindow”对象。
- 使用Proxy对象将“fakeWindow”对象包装成一个代理,该代理将拦截对全局对象的所有访问。
- 在子应用执行时,将代理对象作为全局对象传入,这样子应用对全局对象的访问和修改都将被拦截和重定向到“fakeWindow”对象上。
// 代理沙箱
class LegacySandbox {
// 持续记录新增和修改的全局变量
currentUpdatePropsValueMap = new Map()
// 沙箱期间更新的全局变量
modifiedPropsOriginalValueMapInSandbox = new Map()
// 沙箱期间新增的全局变量
addedPropsMapInSandbox = new Map()
propsWindow = {}
// 核心逻辑
constructor() {
const fakeWindow = Object.create(null)
// 设置值或者获取值
this.propsWindow = new Proxy(fakeWindow, {
set: (target, prop, value, receiver) => {
const originValue = window[prop]
if (!window.hasOwnProperty(prop)) {
this.addedPropsMapInSandbox.set(prop, value)
} else if(!this.modifiedPropsOriginalValueMapInSandbox.has(prop)){
this.modifiedPropsOriginalValueMapInSandbox.set(prop, originValue)
}
this.currentUpdatePropsValueMap.set(prop,value)
window[prop]= value
},
get: (target, prop, receiver) => {
return window[prop]
}
})
}
setWindowProp(prop, value, isToDelete) {
if (value === undefined && isToDelete) {
delete window[prop]
} else {
window[prop] = value
}
}
active() {
// 恢复上一次该微应用处于运行状态时,对window 上做的所有应用的修改
this.currentUpdatePropsValueMap.forEach((value, prop) => {
this.setWindowProp(prop, value)
})
}
// 失活
inactive() {
// 还原window上的属性
this.modifiedPropsOriginalValueMapInSandbox.forEach((value, prop) => {
this.setWindowProp(prop, value)
})
// 删除在微应用运行期间 window 新增的属性
this.addedPropsMapInSandbox.forEach((_, prop) => {
this.setWindowProp(prop, undefined, true)
})
}
}
window.city="beijing"
let LegacySandbox01 = new LegacySandbox()
console.log('11',window.city)
LegacySandbox01.active()
LegacySandbox01.propsWindow.city ="shanghai"
console.log('22',window.city)
LegacySandbox01.inactive()
console.log('33',window.city)
三、快照沙箱
原理概述:
全局对象快照:在子应用加载时,对全局对象进行快照,保存其当前状态。
状态恢复:在子应用卸载时,将全局对象恢复到加载时的状态,以消除子应用对全局对象所做的修改。
具体实现:
- 在子应用加载前,遍历全局对象并保存其属性和值到一个快照对象中。
- 子应用加载并执行过程中,所有对全局对象的修改都只在当前子应用的执行环境中生效。
- 子应用卸载时,遍历全局对象并将其属性和值恢复到快照对象中的状态。
// example
class Sanapshotbox {
// 激活
windowSnapshot = {}
modifyPropsMap = {}
active() {
// 保存window 对象上所有属性的状态
for (const prop in window) {
this.windowSnapshot[prop] = window[prop]
}
// 恢复上一次在运行微应用的时候改过的window 上的属性
Object.keys(this.modifyPropsMap).forEach(prop => {
window[prop] = this.modifyPropsMap[prop]
})
}
// 失活
inactive() {
// 记录修改window 上的哪些属性
for (const prop in window) {
if (window[prop] !== this.windowSnapshot[prop]) {
this.modifyPropsMap[prop] = window[prop]
// 将window 上的属性状态 还原至微应用之前的状态
window[prop] = this.windowSnapshot[prop]
}
}
}
}
window.city="beijing" // 初始值
let sanapshotbox = new Sanapshotbox()
console.log("11",window.city)
sanapshotbox.active() // 微应用运行
window.city="上海"
console.log("22",window.city)
sanapshotbox.inactive() // 微应用卸载了
console.log('33',window.city)
四、选择沙箱机制
Qiankun会根据当前的环境和配置选择使用Proxy沙箱还是快照沙箱,如果浏览器不支持Proxy对象或配置了特定的选项(如singleton: false),则可能会选择使用快照沙箱。
CSS隔离:使用Shadow DOM来实现CSS的隔离。Shadow DOM允许将子应用的DOM和样式封装在一个独立的、隔离的环境中,从而避免子应用之间的样式冲突。
JavaScript隔离:通过沙箱机制,Qiankun能够确保子应用之间的JavaScript执行环境也是隔离的。每个子应用都有自己的“fakeWindow”对象或快照状态,因此它们之间的全局变量和函数不会相互干扰。