importScripts() 的新式替代方案。
背景
ES 模块一直是开发者的最爱。除了许多其他优势之外,它们还承诺提供通用模块格式,这样共享的代码只需发布一次,即可在浏览器和 Node.js 等替代运行时中运行。虽然所有新型浏览器都提供某种 ES 模块支持,但并非所有浏览器都支持在所有可以运行代码的位置运行 ES 模块。具体而言,对在浏览器的 Service Worker 中导入 ES 模块的支持才刚刚开始广泛提供。
本文详细介绍了各大常用浏览器中服务工件对 ES 模块的支持情况,以及一些需要避免的注意事项,以及发布向后兼容的服务工件代码的最佳实践。
使用场景
在服务工件中使用 ES 模块的理想用例是加载与支持 ES 模块的其他运行时共享的现代库或配置代码。
在 ES 模块之前,如果尝试以这种方式共享代码,就需要使用旧版“通用”模块格式(例如包含不必要样板代码的 UMD),并编写用于更改全局公开变量的代码。
如果通过 ES 模块导入的脚本的内容发生变化,则可以触发服务工作器更新流程,与 importScripts()
的行为一致。
当前限制
仅限静态导入
ES 模块可以通过以下两种方式导入:使用 import ... from '...'
语法进行静态导入,或使用 import()
方法进行动态导入。在服务工件内,目前仅支持静态语法。
此限制类似于对 importScripts()
使用施加的类似限制。对 importScripts()
的动态调用无法在服务工件内运行,并且所有 importScripts()
调用(本质上是同步调用)都必须在服务工件完成其 install
阶段之前完成。此限制可确保浏览器知道服务工件实现期间所需的所有 JavaScript 代码,并能够隐式缓存这些代码。
最终,此限制可能会解除,并且可能会允许导入动态 ES 模块。目前,请确保您仅在服务工件中使用静态语法。
其他工人呢?
“专用”Worker 中的 ES 模块(即使用 new Worker('...', {type: 'module'})
构建的模块)的支持更为广泛,从 Chrome 80 版开始,Chrome 和 Edge 都支持此类模块,最新版 Safari 也支持。专用工作器支持静态和动态 ES 模块导入。
从版本 83开始,Chrome 和 Edge 支持共享工作器中的 ES 模块,但目前没有其他浏览器提供此支持。
不支持导入映射
导入映射允许运行时环境重写模块说明符,例如,在可从中加载 ES 模块的首选 CDN 的网址前面附加内容。
虽然 Chrome 和 Edge 89 版及更高版本支持导入映射,但目前无法与服务工作器搭配使用。
浏览器支持
从 91 版开始,Chrome 和 Edge 支持 Service Worker 中的 ES 模块。
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 模块,上述示例将正常运行,但在撰写本文时,情况并非如此。
为了适应不提供内置支持的浏览器,您可以通过与 ES 模块兼容的捆绑工具运行服务工件脚本,以创建包含所有模块代码的内嵌服务工件,并在旧版浏览器中运行。或者,如果您尝试导入的模块已以 IIFE 或 UMD 格式捆绑提供,您可以使用 importScripts()
导入它们。
当您有两个版本的服务工件可用(一个使用 ES 模块,另一个不使用)后,您需要检测当前浏览器支持的版本,并注册相应的服务工件脚本。检测支持的最佳实践目前尚未确定,但您可以关注此 GitHub 问题中的讨论,获取相关建议。
_照片由 Vlado Paunovic 拍摄,选自 Unsplash 网站_