Service Worker 无法动态合并 fetch 响应,而是通过预缓存独立资源+fetch 中按路径编排响应来减少服务器压力;关键在 install 阶段缓存带哈希的 chunk、fetch 中并行读取关联资源并结构化返回、避开 HTML 和非幂等请求缓存,辅以导航预加载优化首屏。

Service Worker 本身不能动态合并多个 fetch 请求返回的资源文件(比如把 a.js 和 b.js 拼成一个新响应体),它没有文件系统操作能力,也不支持运行时生成或修改 JS/CSS 内容。所谓“动态合并”,在微前端或资源聚合场景中,实际指的是按需编排已预缓存的独立资源,并以统一结构返回,从而减少对服务器的重复请求和并发压力。
预缓存是前提:每个子模块必须可单独命中
服务器并发压力大,往往源于页面多次发起细碎请求(如加载 5 个微应用,触发 15+ 个 JS/CSS 请求)。解决思路不是让 SW 合并响应,而是让这些资源在 install 阶段就进入 Cache Storage,后续全部走本地读取:
- 构建时确保每个子模块输出独立 chunk,带内容哈希(如
dashboard.8a3f2d.js、user.css) - 用插件(如
vite-plugin-pwa或WorkboxWebpackPlugin)自动生成 precache 清单,并写入 SW 脚本 - install 事件中调用
caches.open('micro-v1').then(cache => cache.addAll(urls)),确保所有关键静态资产离线即得 - 避免将子模块打包进主包——它们必须能被单独
fetch(),否则无法被 SW 单独缓存和读取
fetch 中按路径编排响应:用组合代替合并
当主应用执行 import('./modules/chart.js') 时,SW 可拦截该请求,在 fetch 事件中不转发给服务器,而是从缓存中并行读取 chart 模块所需的全部配套资源(JS + CSS + locale 数据),再构造一个结构化响应:
- 用正则或路径约定识别微模块请求,例如匹配
/modules/[^/]+\.js$ - 根据请求路径推导关联资源:请求
/modules/chart.js→ 尝试读取/modules/chart.js、/modules/chart.css、/locales/zh-CN/chart.json - 调用
cache.match()并行读取多个 key,用Promise.all()等待结果 - 构造
new Response(JSON.stringify({ js, css, locale }), { headers: { 'Content-Type': 'application/json' } })返回统一格式
降低服务器压力的关键实操细节
真正减少并发的核心不在“合并”,而在消除不必要的网络请求。以下三点直接影响服务器负载:
- HTML 页面不参与预缓存:带服务端注入内容的 HTML(如含用户信息、token)缓存后会导致状态污染,应跳过 install 阶段缓存,改用 fetch 事件中的「缓存优先 + 后台更新」策略
- 非幂等请求直连网络:POST /api/submit、PUT /user/profile 等请求绝不缓存,也不拦截,避免因缓存导致重复提交或数据错乱
-
精准匹配 URL 和 credentials:缓存时若请求带
credentials: 'include',则caches.open()必须显式声明相同凭据模式,否则match()总是失败,最终仍会打到服务器
配合导航预加载进一步压缩请求窗口
冷启动时 SW 初始化有延迟,可能造成首屏请求卡顿。启用导航预加载后,浏览器会在 SW 启动的同时并发拉取资源,显著减少服务器瞬时并发峰值:
- 注册时调用
registration.navigationPreload.enable() - fetch 事件中优先检查
event.preloadResponse,存在则直接返回,不查缓存也不发新请求 - 预加载响应默认走浏览器原生缓存,无需 SW 干预,天然绕过并发瓶颈

















