Service Worker 怎样只缓存同源静态资源
Service Worker 只缓存同源静态资源,关键不是把每一次 fetch 都交给缓存,而是在 fetch 事件里先判断请求的 origin,再用 Request.destination 限定资源类型。命中缓存就直接返回;未命中时请求网络,并且只把状态正常、确实属于静态资源的响应副本放进缓存。
官方地址:https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
- 同源判断使用
new URL(event.request.url).origin === self.location.origin,不要只比较字符串前缀。 - 用
Request.destination允许style、script、image、font等静态资源,排除document、fetch和empty。 - 缓存 API 不会自动按 HTTP 缓存头更新条目,缓存名要版本化,并在
activate中清理旧版本。
先确认 Service Worker 为什么没有接管请求
遇到“缓存没有生效”时,第一步不是修改 cache.put(),而是确认 Service Worker 能否运行。它属于 Worker,不能访问 DOM;注册文件必须来自安全上下文,通常是 HTTPS,开发时的 http://localhost 也被浏览器视为安全上下文。注册脚本的目录还决定默认作用域,脚本放在过深的目录时,页面可能根本不在它的控制范围内。
注册代码可以保持很小,把路径和作用域写清楚:
if ('serviceWorker' in navigator) {
// 注册路径应与实际脚本位置一致,scope 决定它能控制哪些页面。
navigator.serviceWorker.register('/sw.js', { scope: '/' })
.then((registration) => {
console.log('Service Worker registered:', registration.scope);
})
.catch((error) => {
// 注册失败先检查 HTTPS、脚本路径和作用域,而不是假设缓存逻辑有问题。
console.error('Service Worker registration failed:', error);
});
}
首次注册完成后,当前页面不一定马上由新 Worker 控制;页面刷新一次或使用 clients.claim() 都可能影响观察结果。这个现象常被误判成“同源条件没有命中”。

用 origin 和 destination 把缓存范围收窄
真正的筛选逻辑放在 fetch 事件里。new URL(request.url).origin 能把协议、主机和端口作为整体比较;相比用 startsWith('/assets/'),它不会因为路径前缀相似就把外部地址误纳入。
Request.destination 由浏览器描述请求将要用于什么目的,例如样式表、脚本、图片、字体或文档。示例只允许静态资源目的,明确跳过页面导航和接口请求:
const CACHE_NAME = 'static-assets-v1';
const STATIC_DESTINATIONS = new Set([
'style',
'script',
'image',
'font',
'audio',
'video'
]);
self.addEventListener('fetch', (event) => {
const request = event.request;
const requestURL = new URL(request.url);
const sameOrigin = requestURL.origin === self.location.origin;
// 只处理同源静态资源;document、fetch 和空 destination 留给网络层。
if (!sameOrigin || !STATIC_DESTINATIONS.has(request.destination)) {
return;
}
event.respondWith(cacheFirst(request));
});
这里的“同源”包含协议、主机和端口。即使两个地址看起来都来自同一个站点,只要端口不同,也不能当作同源。接口请求的 destination 通常是 empty,但不要只依赖这一点;origin 与资源用途应当同时判断。

缓存优先只保存可以复用的响应
对静态资源可以采用缓存优先:先查当前版本的 Cache,命中就返回;未命中时访问网络,收到成功响应后克隆一份写入缓存,原响应继续返回给页面。因为响应体只能被消费一次,写入缓存和返回页面必须使用不同的副本。
async function cacheFirst(request) {
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(request);
if (cachedResponse) {
// 命中缓存直接返回,避免静态资源每次都访问网络。
return cachedResponse;
}
const networkResponse = await fetch(request);
// 只缓存成功响应,避免把 404 或服务器错误固定在缓存里。
if (networkResponse.ok) {
await cache.put(request, networkResponse.clone());
}
// 返回原响应给页面,clone 只用于保存另一份响应体。
return networkResponse;
}
Cache 的条目不会因为 HTTP 缓存头自动更新,也不会自动过期;应用需要自己决定何时替换或删除它们。若静态文件名不带内容哈希,部署新版本时更要更换缓存名,否则旧脚本或旧样式可能继续被命中。
用版本化缓存处理更新和旧条目
把清理动作放到 activate,保留当前版本,其余名称全部删除。event.waitUntil() 用来延长生命周期,确保异步清理完成前事件不会被过早结束:
self.addEventListener('activate', (event) => {
const keepCaches = new Set([CACHE_NAME]);
event.waitUntil(
caches.keys().then((cacheNames) => Promise.all(
cacheNames.map((cacheName) => {
// 只删除不在当前白名单中的旧缓存,避免误删其他用途的缓存。
if (!keepCaches.has(cacheName)) {
return caches.delete(cacheName);
}
return undefined;
})
))
);
});
排查时可以按这个顺序观察:注册是否成功、当前页面是否已被控制、请求是否同源、destination 是否在白名单、缓存名是否切换、网络响应是否为 ok。任意一层不满足,后面的 Cache.match 都不会给出预期结果。
相关问题
为什么接口也被 Service Worker 拦截了?
如果只监听了 fetch 却没有检查 destination,接口请求同样会进入处理器。为静态缓存建立明确白名单,并让 empty 等类型直接返回。
跨域 CDN 资源能不能放进这个缓存?
本文的目标是只缓存同源资源,所以跨源请求直接跳过。如果业务确实需要缓存 CDN,应单独确认 CORS、响应类型和缓存策略,不要把它混入同源白名单。
为什么改了代码还读到旧文件?
通常是旧缓存名仍在使用、旧 Worker 仍处于 waiting,或当前页面还没有被新 Worker 控制。提升缓存版本并在 activate 清理旧名,再重新加载受控页面。
Linux ulimit 怎样判断文件句柄上限
- 上一篇
- Linux ulimit 怎样判断文件句柄上限
- 下一篇
- CSS :has() 按表单校验状态切换父级样式
-
- 文章 · 前端 | 25分钟前 | 性能优化 · ArrayBuffer TypedArray 前端性能 Web Worker Transferable
- Web Worker 怎样传输大数组减少复制
- 414浏览 收藏
-
- 文章 · 前端 | 27分钟前 | IntersectionObserver 前端性能 rootMargin threshold 滚动监听抖动
- IntersectionObserver 怎样避免滚动监听抖动
- 477浏览 收藏
-
- 文章 · 前端 | 40分钟前 | javascript · JavaScript Fetch ReadableStream 流式响应 TextDecoderStream
- JavaScript ReadableStream 怎样逐块处理响应
- 150浏览 收藏
-
- 文章 · 前端 | 42分钟前 |
- JavaScript fetch 怎样设置超时并可取消
- 100浏览 收藏
-
- 文章 · 前端 | 44分钟前 |
- CSS :has() 按表单校验状态切换父级样式
- 214浏览 收藏
-
- 文章 · 前端 | 16小时前 |
- 前端 WebSocket 退避重连 怎样处理
- 278浏览 收藏
-
- 文章 · 前端 | 16小时前 |
- 前端 Vue watch 深度监听 怎样处理
- 192浏览 收藏
-
- 文章 · 前端 | 16小时前 | React 竞态 Fetch AbortController useEffect 前端异步
- 前端 React useEffect 竞态 怎样处理
- 107浏览 收藏
-
- 文章 · 前端 | 16小时前 |
- 前端 Vite alias 测试失效 怎样处理
- 483浏览 收藏
-
- 文章 · 前端 | 16小时前 |
- 前端 Vite 环境变量前缀 怎样处理
- 161浏览 收藏
-
- 文章 · 前端 | 16小时前 | 前端 · 性能 · javascript · ArrayBuffer postMessage 结构化克隆 Web Worker 前端计算
- 前端 Web Worker 移出计算 怎样处理
- 121浏览 收藏
-
- 文章 · 前端 | 16小时前 |
- 前端 DataView 读取二进制字段 怎样避免字节序错误
- 248浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 615次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 654次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 687次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 649次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 477次使用
-
- JavaScript函数定义及示例详解
- 2025-05-11 502浏览
-
- 智能体安全引领产业升级——国内AI安全产品市场深度分析
- 2026-08-21 501浏览
-
- CSS变量简化按钮悬停效果技巧
- 2026-05-31 501浏览
-
- JavaScript符号类型详解与应用
- 2026-05-31 501浏览
-
- HTML剪贴板复制粘贴怎么用
- 2026-05-26 501浏览

