渐进式 Web 应用:使用工作器

1. 欢迎

在本实验中,您将使用现有的 Web 应用,并添加 Web 工作器以在两个打开的窗口之间共享状态。这是渐进式 Web 应用工作坊的一系列配套 Codelab 中的第八个。上一个 Codelab 是 Service Worker Includes。这是本系列中的最后一个 Codelab。

学习内容

  • 在多个打开的窗口之间添加共享工作器
  • 使用 Comlink 更轻松地处理工作器

注意事项

  • JavaScript

所需条件

2. 进行设置

首先,克隆或下载完成此 Codelab 所需的起始代码:

如果您克隆了代码库,请确保您位于 pwa06--working-with-workers 分支中。该 ZIP 文件还包含相应分支的代码。

此代码库需要 Node.js 14 或更高版本。获得代码后,在代码的文件夹中通过命令行运行 npm ci,以安装所需的所有依赖项。然后,运行 npm start 以启动 Codelab 的开发服务器。

源代码的 README.md 文件提供了对所有分发文件的说明。此外,以下是您将在整个 Codelab 中使用的关键现有文件:

密钥文件

  • js/preview.js - 预览网页 JavaScript 文件
  • js/main.js - 主要应用 JavaScript 文件

3. 编写 Worker

目前,Web 应用的预览功能仅在加载时显示最新内容。理想情况下,它会在用户输入时显示实时预览。这需要编译可能大量的数据,并在两个不同的打开窗口之间传输这些数据。因此,我们不希望在任何打开的窗口的主线程上执行此操作。我们改用共享 Web Worker。

首先,创建一个包含以下代码的 js/worker.js 文件:

import { expose } from 'comlink';
import { marked } from 'marked';

class Compiler {
  state = {
    raw: '',
    compiled: '',
  };
  subscribers = [];

  async set(content) {
    this.state = {
      raw: content,
      compiled: marked(content),
    };

    await Promise.all(this.subscribers.map((s) => s(this.state)));
  }

  subscribe(cb) {
    this.subscribers.push(cb);
  }
}

const compiler = new Compiler();

onconnect = (e) => expose(compiler, e.ports[0]);

说明

此代码设置了一个名为 Compiler 的类,该类允许设置内容,并允许在编译完内容后调用订阅。由于它是共享工作器,因此只能使用此类的一个实例,所以系统会实例化 Compiler 的新实例。然后,为了让从 worker 外部使用此类时感觉顺畅,我们使用 Comlink 来公开编译器实例,从而能够像使用代码中声明的编译器实例一样使用其所有方法。由于这是一个共享工作器,而不是专用工作器,因此需要向所有连接公开。

4. 向 Worker 发送内容

创建工作器后,我们现在需要向其中发送内容。为此,请更新 js/main.js 以执行以下操作:

  • comlink 导入名为 wrap 的导出项
  • 创建一个名为 worker 的新模块型 Shared Worker,将其类型设置为 module,并使用 new URL 模式 (new URL('./worker.js', import.meta.url)) 指向该 Shared Worker
  • 创建一个 compiler 变量,用于对 worker.port 进行 wrap
  • 在编辑器的更新函数 (editor.onUpdate) 中,将内容保存到数据库后,等待 compiler.set 完成,并传入内容

说明

封装 Comlink 导出项后,公开的类方法等内容可以像未跨工作器边界共享一样使用,但现在一切都是异步的。由于这是共享工作器,而不是专用工作器,因此 Comlink 需要封装工作器的端口,而不是工作器本身。现在,每当对编辑器进行更新时,内容都会发送到工作器进行处理!

5. 更新预览页

最后一步是将编译后的内容从共享工作器中提取到预览中!实现此目的的设置大致相同,但由于函数无法在工作器边界之间传递,因此需要改用函数的代理。Comlink 再次为您提供帮助。更新 js/preview.js 以执行以下操作:

  • comlink 中导入名为 wrapproxy 的导出项
  • 创建并封装共享工作器,就像在 js/main.js 中一样
  • 使用代理函数调用编译器的 subscribe 方法,该代理函数将传入数据的 compiled 属性设置为预览区域的内部 HTML

完成后,打开预览,开始在编辑器中输入内容,然后您会惊喜地发现,Markdown 会自动神奇地编译并实时显示在预览区域中,而不会阻塞任何页面的主线程!

6. 恭喜!

您已了解如何使用共享 worker 在多个 PWA 实例之间共享状态。