Web Worker 概述

到目前为止,本课程中的大部分内容都集中在诸如通用 HTML 性能注意事项、资源提示、优化各种资源类型以提高初始页面加载时间和对用户输入的响应能力,以及延迟加载特定资源等概念上。

但是,关于 JavaScript 的性能方面,本课程尚未涵盖一个方面,那就是 Web Worker 在提高输入响应能力方面的作用,这是本模块和下一个模块涵盖的内容。

JavaScript 通常被描述为单线程语言。实际上,这指的是主线程,它是浏览器完成您在浏览器中看到的大部分工作的单线程。这项工作包括与脚本、某些类型的渲染工作、HTML 和 CSS 解析以及其他驱动用户体验的面向用户的工作相关的任务。实际上,浏览器确实使用其他线程来完成您(开发人员)通常无法直接访问的工作,例如GPU 线程

就 JavaScript 而言,您通常被限制在主线程上执行工作,但这只是默认情况。可以在 JavaScript 中注册和使用其他线程。允许 JavaScript 中多线程的功能称为 Web Worker API

当您有计算密集型工作无法在主线程上运行而不会导致长时间任务使页面无响应时,Web Worker 非常有用。此类任务肯定会影响您网站的交互到下次绘制 (INP),因此了解何时可以完全在主线程之外完成工作可能会有所帮助。这样做可以为主线程上的其他任务创建更多空间,从而加快用户交互速度。

本模块和后续演示具体用例的演示涵盖了 Web Worker。该演示本身展示了如何使用 Web Worker 从主线程外读取 JPEG 文件中的图像元数据,以及如何将该元数据返回到主线程以供用户查看。

如何启动 Web Worker

通过实例化 Worker来注册 Web Worker。当您这样做时,您需要指定 Web Worker 代码所在的位置,浏览器会加载该代码并随后为其创建一个新线程。生成的线程通常称为工作线程

const myWebWorker = new Worker('/js/my-web-worker.js');

在 Worker 的 JavaScript 文件(在本例中为 my-web-worker.js)中,您可以编写随后在单独的工作线程中运行的代码。

Web Worker 限制

与在主线程上运行的 JavaScript 不同,Web Worker 无法直接访问 window 上下文,并且对它提供的 API 的访问权限有限。Web Worker 受以下约束:

  • Web Worker 无法直接访问 DOM。
  • Web Worker 可以通过消息传递管道与 window 上下文通信,这意味着 Web Worker 可以间接方式访问 DOM。
  • Web Worker 作用域是 self,而不是 window
  • Web Worker 作用域确实可以访问 JavaScript 原语和构造,以及诸如 fetch相当多的其他 API 之类的 API。

Web Worker 如何与 window 通信

Web Worker 可以通过消息传递管道与主线程的 window 上下文通信。此管道允许您在主线程和 Web Worker 之间传送数据。要将数据从 Web Worker 发送到主线程,请在 Web Worker 的上下文 (self) 上设置 message 事件

// my-web-worker.js
self.addEventListener("message", () => {
  // Sends a message of "Hellow, window!" from the web worker:
  self.postMessage("Hello, window!");
});

然后在主线程的 window 上下文中的脚本中,您可以使用另一个 message 事件接收来自 Web Worker 线程的消息

// scripts.js

// Creates the web worker:
const myWebWorker = new Worker('/js/my-web-worker.js');

// Adds an event listener on the web worker instance that listens for messages:
myWebWorker.addEventListener("message", ({ data }) => {
  // Echoes "Hello, window!" to the console from the worker.
  console.log(data);
});

Web Worker 的消息传递管道是 Web Worker 上下文的一种逃生出口。使用它,您可以将数据从 Web Worker 发送到 window,您可以使用这些数据来更新 DOM,或执行必须在主线程上完成的其他工作。

测试您的知识

Web Worker 在哪个线程上运行?

主线程。
再试一次。
它自己的线程(也称为Web Worker 线程)。
正确!
GPU 线程。
再试一次。

Web Worker 可以访问什么?

JavaScript 原语,例如数组和对象。
正确!
window 上下文中可用的 API 的严格子集,包括 fetch
正确!
window 上下文,但只能间接访问。
正确!

Web Worker 如何访问 `window` 上下文?

直接通过引用 window 对象的成员。
再试一次。
Web Worker 无法通过任何方式访问 window
再试一次。
通过由 Web Worker 上下文 (self) 中的 postMessage 方法促进的消息传递管道。
正确!

下一步:一个具体的 Web Worker 用例

下一个模块中,详细介绍并演示了一个具体的 Web Worker 用例。在该模块中,Web Worker 用于从给定的 URL 获取 JPEG 文件,并在 Web Worker 中读取其 Exif 元数据。然后,该数据被发送回主线程以显示给用户。