(JS隔离)的几种方案带来的思考和展望

背景介绍:

从第五代标准HTML推广发布后,其中工作线程(Web Worker)概念的推出让人眼前一亮,但未曾随之激起多大的浪花,并被在其随后工程侧的 Angular、Vue、React 等框架的「革命」浪潮所淹没。
但自从2019 年爆火的微前端架构的出现,基于微应用间 JavaScript 沙箱隔离的需求,Web Worker 才得以重新从边缘化的位置跃入到我的中心视野。

什么是JS沙箱:

在现实与 JavaScript 相关的场景中,我们知道平时使用的浏览器就是一个沙箱,运行在浏览器中的 JavaScript 代码无法直接访问文件系统、显示器或其他任何硬件。Chrome 浏览器中每个标签页也是一个沙箱,各个标签页内的数据无法直接相互影响,接口都在独立的上下文中运行。而在同一个浏览器标签页下运行 HTML 页面,有哪些更细节的、对沙箱现象有需求的场景呢?

当我们作为前端开发人员较长一段时间后,我们很轻易地就能想到在同一个页面下,使用沙箱需求的诸多应用场景,譬如:

  1. 执行从不受信的源获取到的第三方 JavaScript 代码时(比如引入插件、处理 jsonp 请求回来的数据等)。
  2. 在线代码编辑器场景(比如著名的 codesandbox)。
  3. 使用服务端渲染方案。
  4. 模板字符串中的表达式的计算。
  5. ……

JS沙箱的条件:

既然有JS沙箱的需求出现,那么我们就要整理一下什么样的沙箱是我们想要的,我整理了以下几种条件:

  1. 挂在 window 上的全局方法/变量(如 setTimeout、滚动等全局事件监听等)在子应用切换时的清理和还原。
  2. Cookie、LocalStorage 等的读写安全策略限制。
  3. 各子应用独立路由的实现。
  4. 多个微应用共存时相互独立的实现

很显而易见的是,只要符合以上几种条件就是符合我们需求的JS沙箱,那么市面上有几种解决方案呢?

JS隔离的几种方式:

谈到JS沙箱,市场上真正能落地的产品不多,从「iframe」到「single-spa」,但其中的解决方案都有各自的缺点。
阿里的乾坤方案,说实话,以我的技术水平不好评论什么。
这里引入一下「绯凡」大佬在简书上的评价吧
在这里插入图片描述
当然,从「绯凡」的回答中也可以看出来,阿里正在做一系列的微前端架构的产品,我们相信,各个大厂商也在这一领域有着自己的解决方案和思考。

大概归拢了以下几种方案:

  1. window快照还原:利用 window 上常用的常量和方法以及不支持 Proxy时降级通过快照实现备份还原,这一方案是比较原始的超脱方案,乾坤框架应该也有这种方案的影子,从沙箱有两个 入口可以看出来(一个是proxySandbox.ts,另一个是snapshotSandbox.ts),结合其相关开源文章分享,简单总结下其实现思路:起初版本使用了快照沙箱的概念,模拟
    ES6 的 Proxy API,通过代理劫持 window ,当子应用修改或使用
    window上的属性或方法时,把对应的操作记录下来,每次子应用挂载/卸载时生成快照,当再次从外部切换到当前子应用时,再从记录的快照中恢复,而后来为了兼容多个子应用共存的情况,又基于Proxy
    实现了代理所有全局性的常量和方法接口,为每个子应用构造了独立的运行环境。
  1. 第二种方案就是阿里云开发平台的 Browser VM借助iframe的方案,大概原理就是接触iframe生成window,将子应用的包装成一个闭包方法,将iframe生成的window传入,借助iframe天生的优势(硬隔离),非常nice的解决了各个子应用的隔离,也不需要自行维护window这种情况,但缺点也很明显,所有执行必须通过postMessage
    与主线程通信,易用性以及 postMessage 序列化带来的性能等问题都深深困扰的开发者。
  1. 第三种方案是Figma 采用的方案,它基于目前还在草案阶段 Realm API,并将 JavaScript 解释器的一种 C++ 实现 Duktape 编译到了 WebAssembly,然后将其嵌入到
    Realm上下文中,实现了其产品下的三方插件的独立运行。这种方案和探索的基于 Web
    Worker的实现可能能够结合得更好,值得注意的是,Realm 同样可以使用 JavaScript 目前已有的特性来实现,即 with
    与Proxy。这也是目前社区比较流行的沙箱方案。

小结:

JavaScript 沙箱隔离在社区是个经久不衰的话题,最简单的 iframe 标签 Sandbox 属性就已经能做到 JavaScript 运行时的隔离,社区较为流行的是利用一些语言特性(with、realm、Proxy 等 API )屏蔽(或代理) Window、Document 等全局对象,建立白名单机制,对可能潜在危险操作的 API 重写(如阿里云 Console OS - Browser VM),另一种就是更高端的操作了,就好像Figma一样,使用这种尝试嵌入平台无关的 JavaScript 解释器,所有第三方代码都通过嵌入的解释器来执行。

当然了,完整的微前端一定不是单单一个框架领域就能解决的,一定是一套完整的架构,对于前端来说,甚至于到一整套的开发工具插件,一整套的开发模式,这些模式一定会对Vue这种框架产生一定量的冲击,我们目前看到前端体系的一步步完整,各种优秀的框架如雨后春笋一样发展,另一方面,CSS 隔离也比想象中复杂,而且大部分的方案会引入额外的问题