Service Worker 中的 ES 模块

importScripts() 的现代替代方案。

背景

ES 模块 已有一段时间被开发者最青睐。除了 许多其他优势 它们提供一种通用模块格式, 一次发布,可在浏览器和替代运行时(例如 Node.js。虽然 所有新型浏览器 提供一些 ES 模块支持,但并非在所有地方都提供支持 可以运行具体而言,支持导入 浏览器的 Service Worker

本文详细介绍了 Service Worker 中 ES 模块支持的当前状态 以及需要避免的一些陷阱, 交付向后兼容的 Service Worker 代码。

使用场景

Service Worker 内 ES 模块的理想应用场景是: 与其他运行时共享的现代库或配置代码 支持 ES 模块。

在 ES 模块之前尝试以这种方式共享代码会导致使用旧版模块 "通用"模块格式,例如 UMD,这些模块包括 不需要的样板文件,以及编写对全局公开代码进行更改的代码 变量。

通过 ES 模块导入的脚本可以触发 Service Worker 更新 会改变其内容,从而匹配 行为 / importScripts()

当前限制

仅限静态导入

ES 模块可通过以下两种方式之一导入: 静态地 使用 import ... from '...' 语法,或者 动态, 使用 import() 方法。在 Service Worker 内,只有静态 语法。

此限制类似于 类似限制importScripts() 的使用施加了限制。不会动态调用 importScripts() 在 Service Worker 内部工作,并且所有 importScripts() 调用(也就是 本质上是同步的,必须在 Service Worker 完成其 install 阶段。此限制可确保浏览器了解并 能够隐式缓存 Service Worker 所需的全部 JavaScript 代码, 在安装过程中实施。

最终,这一限制可能会取消, 模块导入 可能允许。 目前,请确保您仅在 Service Worker。

其他工作器呢?

支持 “专用”模块中的 ES 模块工作器数量 - 使用 new Worker('...', {type: 'module'}) 构建的应用更加广泛,并且 在 Chrome 和 Edge 中 版本 80,以及 最新版本的 Safari。 专用 worker 同时支持静态和动态 ES 模块导入。

Chrome 和 Edge 在 共享工作器版本 83 开始,但不 其他浏览器目前提供支持。

不支持导入地图

允许导入地图 运行时环境来重写模块说明符,例如将模块说明符 可从中加载 ES 模块的首选 CDN 的网址。

而 Chrome 和 Edge 版本 89 及更高版本 支持导入地图, 无法用于服务 worker。

浏览器支持

Chrome 和 Edge 支持 Service Worker 中的 ES 模块,从 91 版

Safari 在 技术预览版 122, 开发者应该会在 Safari 版本。

示例代码

这是在 Web 应用的 window 中使用共享 ES 模块的基本示例 上下文,同时还要注册使用同一 ES 模块的 Service Worker:

// Inside config.js:
export const cacheName = 'my-cache';
// Inside your web app:
<script type="module">
  import {cacheName} from './config.js';
  // Do something with cacheName.

  await navigator.serviceWorker.register('es-module-sw.js', {
    type: 'module',
  });
</script>
// Inside es-module-sw.js:
import {cacheName} from './config.js';

self.addEventListener('install', (event) => {
  event.waitUntil((async () => {
    const cache = await caches.open(cacheName);
    // ...
  })());
});

向后兼容性

如果所有浏览器都支持以上示例中的 ES 模块, Service Worker,但在撰写本文时,情况并非如此。

要适应没有内置支持的浏览器,您可以 通过 Service Worker 脚本 ES 模块兼容打包器 - 用于创建 Service Worker,其中包含所有内嵌的模块代码, 版本较低的浏览器。另外,如果您要导入的模块 捆绑在 IIFEUMD 格式,您可以使用 importScripts()

获得两个版本的 Service Worker 后,其中一个版本使用 ES 其他模块,则您需要检测当前 并注册相应的 Service Worker 脚本。最佳 目前有多种方法用于检测支持情况,但您可以按照 讨论 GitHub 问题 建议。

_摄影:Vlado PaunovicUnsplash 网站发布