现在,使用 Web Worker 中的 JavaScript 模块可以更轻松地将繁重工作转移到后台线程。
JavaScript 是单线程的,这意味着它一次只能执行一项操作。这是 非常直观,适用于网络上的很多情况,但在我们需要 处理繁重的任务,如数据处理、解析、计算或分析。随着 在 Web 上分发复杂应用后,对多线程的 处理。
在网络平台上,线程处理和并行处理的主要基元是 Web Workers API。 工作器是操作系统之上的轻量级抽象概念 一个线程来公开传递消息传递 API 实现线程间通信在执行代价高昂的计算或 在大型数据集上运行,这使得主线程在执行 在一个或多个后台线程上执行开销大的操作。
以下是一个典型的工作器使用示例,其中工作器脚本监听来自主实例的消息 会话,并通过回发自己的消息来进行响应:
page.js:
const worker = new Worker('worker.js');
worker.addEventListener('message', e => {
console.log(e.data);
});
worker.postMessage('hello');
worker.js:
addEventListener('message', e => {
if (e.data === 'hello') {
postMessage('world');
}
});
在大多数浏览器中,Web Worker API 已经使用了十多年。虽然 这也意味着工作者拥有极佳的浏览器支持和经过精心优化,这也意味着他们 JavaScript 模块之前的版本。由于设计 worker 时没有模块系统,因此 API 用于将代码加载到 Worker,并且编写脚本与使用同步脚本 2009 年的常见加载方法。
历史记录:传统版工作器
Worker 构造函数采用经典的
脚本网址,即
相对于文档网址它会立即返回对新工作器实例的引用,
它公开了一个消息传递接口以及一个 terminate()
方法,该方法会立即停止并停止响应
会销毁 Worker。
const worker = new Worker('worker.js');
Web Worker 中提供了 importScripts()
函数,用于加载其他代码,但
暂停 worker 的执行,以便获取和评估每个脚本。它还负责执行脚本
像传统的 <script>
标记一样,这意味着一个脚本中的变量可以
被另一个中的变量覆盖
worker.js:
importScripts('greet.js');
// ^ could block for seconds
addEventListener('message', e => {
postMessage(sayHello());
});
greet.js:
// global to the whole worker
function sayHello() {
return 'world';
}
因此,Web Worker 向来对网络架构的
应用。开发者必须打造巧妙的工具和解决方法,
使用 Web Worker,而无需放弃现代开发实践。例如,
webpack 将一个小型模块加载器实现嵌入到使用 importScripts()
的生成代码中
但会将模块封装在函数中,以避免变量冲突,并模拟
依赖项导入和导出
输入模块 worker
为 Web Worker 提供了一种新模式,具有 JavaScript 的工效学设计和性能优势
模块(称为模块工作器)在 Chrome 80 中内置。通过
Worker
构造函数现在接受新的 {type:"module"}
选项,该选项会更改脚本加载和
以便与 <script type="module">
匹配。
const worker = new Worker('worker.js', {
type: 'module'
});
由于模块工作器是标准的 JavaScript 模块,因此它们可以使用 import 和 export 语句。如 对于所有 JavaScript 模块,依赖项仅在给定上下文(主线程、 worker 等),而所有后续导入都会引用已执行的模块实例。加载 JavaScript 模块的执行和执行也由浏览器进行优化。模块的依赖项可以是 在模块执行之前加载,这样可以将整个模块树 并行运行。模块加载还会缓存已解析的代码,这意味着主 在 Worker 中,只需要解析一次。
迁移到 JavaScript 模块还允许使用动态
import 用于延迟加载代码而不阻止
worker。动态导入比使用 importScripts()
加载依赖项要明确得多,
因为系统返回的是导入的模块的导出,而不是依赖于全局变量。
worker.js:
import { sayHello } from './greet.js';
addEventListener('message', e => {
postMessage(sayHello());
});
greet.js:
import greetings from './data.js';
export function sayHello() {
return greetings.hello;
}
为了确保出色的性能,旧版 importScripts()
方法在模块中不可用
worker。将 worker 切换为使用 JavaScript 模块意味着所有代码都以严格的
模式。另一个
值得注意的变化是,JavaScript 模块的顶级作用域内 this
的值改为
undefined
,而在传统版 worker 中,该值是 worker 的全局范围。幸运的是,
始终是一个 self
全局变量,提供对全局范围的引用。该服务已在以下国家/地区推出:
所有类型的 worker(包括 Service Worker)以及 DOM 中。
使用 modulepreload
预加载工作器
模块工作器的一项实质性性能改进是能够预加载
worker 及其依赖项。使用模块工作器时,脚本会作为标准加载和执行
JavaScript 模块,这意味着可以使用 modulepreload
预加载甚至预先解析这些模块:
<!-- preloads worker.js and its dependencies: -->
<link rel="modulepreload" href="worker.js">
<script>
addEventListener('load', () => {
// our worker code is likely already parsed and ready to execute!
const worker = new Worker('worker.js', { type: 'module' });
});
</script>
主线程和模块 worker 也可以使用预加载的模块。这对于 在两种上下文中都导入的模块,或者在无法提前知道的情况下 某个模块将用在主线程上还是在 worker 中使用。
以前,用于预加载 Web Worker 脚本的选项有限,
必须可靠。传统工作器有自己的“工作器”可预加载的资源类型,但无
浏览器实现了 <link rel="preload" as="worker">
。因此,主要技术
那就是使用 <link rel="prefetch">
,它完全依赖于
HTTP 缓存中当与正确的缓存标头结合使用时,这使得这成为可能
避免工作器实例化必须等待下载工作器脚本。但是,与
modulepreload
,此方法不支持预加载依赖项或准备解析。
共享工作器呢?
共享工作器
已更新为从 Chrome 83 开始支持 JavaScript 模块。与专用工作器一样,
使用 {type:"module"}
选项构建共享 worker 现在会将 worker 脚本作为
模块,而不是传统脚本:
const worker = new SharedWorker('/worker.js', {
type: 'module'
});
在支持 JavaScript 模块之前,SharedWorker()
构造函数只需要
网址和可选的 name
参数。这仍可用于传统共享工作器;不过
创建模块共享 worker 需要使用新的 options
参数。可用的
选项
与专用 Worker 的相同,包括取代 name
选项,
上一个 name
参数。
Service Worker 呢?
Service Worker 规范
已更新,以支持接受
将 JavaScript 模块作为入口点,并使用与模块工作器相同的 {type:"module"}
选项,
但这项更改尚未在浏览器中实施。完成后,就可以
使用以下代码通过 JavaScript 模块实例化 Service Worker:
navigator.serviceWorker.register('/sw.js', {
type: 'module'
});
由于该规范已更新,因此浏览器将开始实现新行为。 这需要一些时间,因为引入 JavaScript 会带来一些额外的复杂问题 Service Worker。Service Worker 注册需要比较导入的脚本 确定是否触发更新,并且需要针对 JavaScript 模块实现这一点 当用于 Service Worker 时。此外,Service Worker 需要能够绕过 对脚本进行缓存 检查更新。
其他资源和补充阅读材料
- 功能状态、浏览器共识和标准化
- 原始模块工作器规范新增内容
- 适用于共享 worker 的 JavaScript 模块
- 适用于 Service Worker 的 JavaScript 模块:Chrome 实现状态