esm在service worker中不直接执行而仅负责分发,需通过fetch+url.createobjecturl+import()动态加载;应按integrity、内容哈希或语义版本分层缓存;代理时依据request.destination==='script'识别并精细化处理;构建需输出模块清单并预缓存关键路径。

模块脚本(ESM)在 Service Worker 中的资源分发,核心在于精准控制加载时机、缓存策略与网络代理逻辑,避免重复解析、执行阻塞和跨域失效问题。
明确模块脚本的加载生命周期
Service Worker 无法直接 import 模块脚本(因不支持顶层 await 或 ESM 环境),但可通过 importScripts() 加载传统脚本,或用 fetch() 获取模块内容后动态创建 URL.createObjectURL(new Blob([content], {type: 'application/javascript'})) 再通过 import() 加载。关键点是:模块的解析、链接、实例化发生在主线程,Service Worker 只负责“提供”而非“执行”。因此优化重点不在 SW 内执行模块,而在它如何高效、准确地响应模块请求。
- 对
.js请求做 MIME 类型判断(Content-Type: application/javascript或含module的type属性) - 区分静态模块(如
/app.mjs)与动态导入目标(如import('./feature.mjs')),后者常带查询参数或哈希,需匹配通配规则 - 避免在 SW 中对模块做语法转换(如转 CommonJS),应在构建阶段完成,SW 只做透传或缓存决策
缓存策略按模块特性分层设计
模块脚本具有强版本敏感性(依赖图固定、不可热替换),不适合用 Stale-While-Revalidate。应结合完整性校验与版本标识,实现“缓存即正确”:
- 优先匹配
integrity属性:SW 在fetch事件中读取请求 header 或 HTML 中的integrity值,仅当缓存资源的 SHA256 匹配时才返回,否则回源 - 对带内容哈希的路径(如
/chunk.a1b2c3d4.mjs)启用 Cache-First,无需额外校验,文件名即版本 - 对无哈希但带语义版本号的模块(如
/lib/react@18.2.0.mjs),配合Cache-Control: immutable,并设置较长 max-age(如 1 年)
代理逻辑聚焦请求上下文识别
模块脚本的 fetch 请求源自 import() 或 <script type="module"></script>,其 destination 为 "script",且通常携带 sec-fetch-dest: script。利用该特征可精细化代理:
- 在
fetch事件中检查event.request.destination === 'script'和event.request.headers.get('accept')?.includes('javascript') - 对模块请求跳过 HTML 渲染逻辑(如不走 fallback 页面),直接走资源分发链路
- 若模块来自第三方 CDN(如
https://cdn.example.com/react.mjs),可在 SW 中预建立 CORS 代理通道,添加Access-Control-Allow-Origin: *响应头(仅限同源或显式许可场景)
构建与部署协同优化
Service Worker 的能力受限于构建产物结构。需在打包阶段预留策略接口:
- 生成模块清单(
manifest.json)包含路径、哈希、依赖关系,供 SW 动态加载时查表校验 - 将模块入口统一收口到一个逻辑路径(如
/mod/:name),由 SW 解析 name 后映射真实资源,便于灰度或 A/B 分发 - 对动态 import 路径做静态分析,提取所有可能模块路径,预缓存关键子集(非全量),减少首次白屏延迟
不复杂但容易忽略:模块脚本的错误不会触发 SW 的 fetch 失败回调(因为 fetch 成功但执行时报错),所以监控需结合 window.onerror 与 import() 的 catch 链,再上报至 SW 统计接口。











