<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//如果应用 加载 刚开始我加载A应用 window.a B应用 window.a
//当应用切换 沙箱 创造一个干净的环境 给这个子应用使用,当切换时 可以选择丢弃属性和恢复属性
//JS沙箱 proxy(要考虑浏览器是否支持)
//快照沙箱 前后比对 把区别保存起来 再回到之前
class SnapshotSandbox {
constructor() {
this.proxy = window; //window属性
this.modifyPropsMap = {}; //记录在window上的修改
this.active();
}
//激活
active() {
this.windowSnapshot = {}; //拍照
for (const prop in window) {
if (window.hasOwnProperty(prop)) {
this.windowSnapshot[prop] = window[prop]
}
Object.keys(this.modifyPropsMap).forEach(p => {
window[p] = this.modifyPropsMap[p]
})
}
}
//失活
inactive() {
for (const prop in window) {
if (window.hasOwnProperty(prop)) {
if (window[prop] != this.windowSnapshot[prop]) {
this.modifyPropsMap[prop] = window[prop];
window[prop] = this.windowSnapshot[prop];
}
}
}
}
}
let sandbox = new SnapshotSandbox();
//应用的开始到结束 切换后不能改变全局
((window) => {
window.a = 1
window.b = 2
console.log(window.a, window.b)
sandbox.inactive()
console.log('inactive:', window.a, window.b)
sandbox.active() //失活后再还原
console.log('active:', window.a, window.b)
})(sandbox.proxy)
//如果是多个子应用 就不能用这个方式 可以使用es6 proxy
//代理沙箱 可以实现多应用沙箱 把不同的应用使用不同的代理来处理
</script>
</body>
</html>
沙箱在微前端架构中不是必须要做的事情,因为如果规范做的足够好,是能够避免掉一些变量冲突读写,CSS 样式冲突的情况。但是如果你在一个足够大的体系中,仅仅通过规范来保证应用的可靠性面临较大的风险,还是需要技术手段去治理运行时的一些冲突问题,这个也是沙箱方案成为微前端技术体系的一部分原因。
传统的js沙箱主要用于执行一些不可信任的js脚本,其对沙箱的包装只需要一个可执行的js环境即可,一般会屏蔽对location document等重要全局对象的访问,同时一般为一次性执行,执行完第三方脚本后会释放沙箱环境。微前端领域的沙箱对于提出了更高的诉求,需要可能访问几乎所有的全局对象,因为我们很难约束一个子应用在开发过程中使用的全局变量。需要同时支持多个沙箱环境存在,每个沙箱需要有加载、卸载、再次恢复的能力,其对应着微应用的运行生命周期。
在主流的微前端方案中,有一个关键点决定了沙箱如何做:同一时刻是单实例还是多实例存在宿主应用中。这决定了沙箱的复杂度和技术实现。
• 单实例:同一个时刻只有一个微应用实例存在,此刻浏览器所有浏览器资源都是这个应用独占的,这种方案要解决的很大程度是应用切换的时候的变量污染清理与应用再次启动时的变量恢复。这种一般通过全局对象的代理来实现。
• 多实例:资源不是应用独占,需要解决资源共享的问题,比如路由,样式,全局变量读写,DOM。这种情况下不同沙盒需要共享着一些全局变量,甚至涉及到不同微应用间的通信诉求。实现起来一般比较复杂,容易造成变量的全局冲突。