浅探 Web Worker 与 JavaScript 沙箱
作者:ES2049 / 靳志凯
一些「炒冷饭」的背景介绍
本文并不会从头开始介绍 Web Worker 的基础知识和基本 API 的使用等(只是部分有涉及),若还未了解过 Web Worker,可参考查阅 W3C 标准 Workers 文档中的相关介绍。
JavaScript 沙箱
执行从不受信的源获取到的第三方 JavaScript 代码时(比如引入插件、处理 jsonp 请求回来的数据等)。 在线代码编辑器场景(比如著名的 codesandbox)。 使用服务端渲染方案。 模板字符串中的表达式的计算。 ... ...
挂在 window 上的全局方法/变量(如 setTimeout、滚动等全局事件监听等)在子应用切换时的清理和还原。 Cookie、LocalStorage 等的读写安全策略限制。 各子应用独立路由的实现。 多个微应用共存时相互独立的实现。
借鉴 with
的实现效果,在 webpack 编译打包阶段为每个子应用代码包裹一层代码(见其插件包 breezr-plugin-os 下相关文件),创建一个闭包,传入自己模拟的 window、document、location、history 等全局对象。在模拟的 Context 中,new 一个 iframe 对象,提供一个和宿主应用空的(about:blank) 同域 URL 来作为这个 iframe 初始加载的 URL(空的 URL 不会发生资源加载,但是会产生和这个 iframe 中关联的 history 不能被操作的问题,这时路由的变换只支持 hash 模式),然后将其下的原生浏览器对象通过 contentWindow
取出来(因为 iframe 对象天然隔离,这里省去了自己 Mock 实现所有 API 的成本)。取出对应的 iframe 中原生的对象之后,继续对特定需要隔离的对象生成对应的 Proxy,然后对一些属性获取和属性设置,做一些特定的实现(比如 window.document 需要返回特定的沙箱 document 而不是当前浏览器的document 等)。 为了文档内容能够被加载在同一个 DOM 树上,对于 document,大部分的 DOM 操作的属性和方法仍旧直接使用宿主浏览器中的 document 的属性和方法处理等。
Web Worker 与 DOM 渲染
__WRAP_WORKER__(`/* 打包代码 */ }`);
function __WRAP_WORKER__(appCode) {
var blob = new Blob([appCode]);
var appWorker = new Worker(window.URL.createObjectURL(blob));
}
出于线程安全设计考虑,Web Worker 不支持 DOM 操作,必须通过 postMessage 通知 UI 主线程来实现。 Web Worker 无法访问 window、document 之类的浏览器全局对象。
React Worker DOM
AMP WorkerDOM
const code = `
'use strict';
(function(){
${workerDOMScript}
self['window'] = self;
var workerDOM = WorkerThread.workerDOM;
WorkerThread.hydrate(
workerDOM.document,
${JSON.stringify(strings)},
${JSON.stringify(skeleton)},
${JSON.stringify(cssKeys)},
${JSON.stringify(globalEventHandlerKeys)},
[${window.innerWidth}, ${window.innerHeight}],
${JSON.stringify(localStorageInit)},
${JSON.stringify(sessionStorageInit)}
);
workerDOM.document[${TransferrableKeys.observe}](this);
Object.keys(workerDOM).forEach(function(k){self[k]=workerDOM[k]});
}).call(self);
${authorScript}
//# sourceURL=${encodeURI(config.authorURL)}`;
this[TransferrableKeys.worker] = new Worker(URL.createObjectURL(new Blob([code])));
小结与一些个人前瞻
评论