在微前端领域当中,沙箱是很重要的一件事情。本文就浅谈前端JS沙箱实现的几种方式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
目录
前言
在微前端领域当中,沙箱是很重要的一件事情。像微前端框架single-spa没有实现js沙箱,我们在构建大型微前端应用的时候,很容易造成一些变量的冲突,对应用的可靠性面临巨大的风险。在微前端当中,有一些全局对象在所有的应用中需要共享,如document,location,等对象。子应用开发的过程中可能是多个团队在做,很难约束他们使用全局变量。有些页面可能会有多个不同的子应用,需要我们支持多沙箱,每个沙箱需要有加载,卸载,在恢复的能力。
iframe实现沙箱
在前端中,有一个比较重要的html标签iframe,实际上,我们可以通过iframe对象,把原生浏览器对象通过contentWindow取出来,这个对象天然具有所有的属性,而且与主应用的环境隔离。下面我们通过代码看下
1 2 3 |
|
注意:只有同域的ifame才能取出对应的contentWindow, iframe的src设置www.1818xinwen.com为about:blank,可以保证一定是同域的,也不会发生资源加载,参考iframe src
在前言中我们提到,微前端除了有一个隔离的window环境外,其实还需要共享一些全局对象,这时候我们可以用代理去实现。下面我们通过代码看下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
我们运行起来,看下结果
以上我们利用iframe沙箱可以实现以下特性:
- 全局变量隔离,如setTimeout、location、react不同版本隔离
- 路由隔离,应用可以实现独立路由,也可以共享全局路由
- 多实例,可以同时存在多个独立的微应用同时运行
diff方式实现沙箱
在不支持代理的浏览器中,我们可以通过diff的方式实习沙箱。在应用运行的时候保存一个快照window对象,将当前window对象的全部属性都复制到快照对象上,子应用卸载的时候将window对象修改做个diff,将不同的属性用个modifyMap保存起来,再次挂载的时候再加上这些修改的属性。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
我们运行一下,查看结果
这种方式也无法支持多实例,因为运行期间所有的属性都是保存在window上的。
基于代理(Proxy)实现单实例沙箱
在ES6当中,我们可以通过代理(Proxy)实现对象的劫持。基本实录也是通过window对象的修改进行记录,在卸载时删除这些记录,在应用再次激活时恢复这些记录,来达到模拟沙箱环境的目的。代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
|
我们运行代码,看下结果
这种方式同一时刻只能有一个激活的沙箱,否则全局对象上的变量会有两个以上的沙箱更新,造成全局变量冲突。
基于代理(Proxy)实现多实例沙箱
在单实例的场景总,我们的fakeWindow是一个空的对象,其没有任何储存变量的功能,微应用创建的变量最终实际都是挂载在window上的,这就限制了同一时刻不能有两个激活的微应用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
|
运行代码,结果如下:
这种方式同一时刻只能有一个激活的多个沙箱,从而实现多实例沙箱。
结束语
以上是微前端比较常用的沙箱实现方式,想要在生产中使用,需要我们做很多的判断和约束。下篇我们通过源码看下微前端框架qiankun是怎么实现沙箱的。上面的代码在github,如需查看,请移步js-sandbox
参考
到此这篇关于浅谈前端JS沙箱实现的几种方式的文章