App Shell初次加载后内容不更新,如何排查?
我在做PWA的App Shell架构时遇到奇怪问题,页面首次加载显示正常,但刷新后动态内容没更新,控制台也没报错。检查了sw.js的fetch拦截逻辑,尝试过清除缓存也不行,这是怎么回事?
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/data')) {
event.respondWith(
caches.match(event.request)
.then(cache => cache || fetch(event.request))
);
}
});
服务工作线程里用了这样的缓存策略,但数据接口的响应明明有更新,前端页面还是显示旧数据。难道是缓存策略写错了?或者需要设置Cache-Control头?
---
### 1. 当前代码的问题分析
你的
fetch监听逻辑是这样的:这段代码的意思是:
- 如果请求的是
/api/data接口- 那么先从缓存里找匹配的请求
- 如果找到了,就返回缓存数据
- 如果没找到,再去网络拉取
**这会导致一个典型问题:缓存优先(Cache-first)策略会一直返回旧数据,除非缓存过期或被清除。**
而你的问题是「刷新后动态内容没更新」,说明你期望的是每次刷新都拉取最新数据,但你用的是「先查缓存」的逻辑,这显然不符合预期。
---
### 2. 解决方案:选择正确的缓存策略
如果你希望每次刷新都获取最新数据,但又想利用缓存做离线兜底,建议使用「网络优先(Network-first)」策略。
这样做的逻辑是:
- 先尝试从网络请求最新数据
- 如果失败(比如离线),再从缓存里取数据兜底
- 这样刷新后就能拿到最新数据了,而不是优先用缓存
---
### 3. 如果你确实需要缓存,但想控制缓存更新时间
你可以考虑加上一个「缓存失效时间」的判断。但 Service Worker 本身没有自动过期机制,所以需要你自己写缓存策略来控制。
比如用 Cache-Control 或 Expires 头,或者自己写一个带时间戳的缓存 key。
举个例子,你可以结合
cache-control响应头来控制缓存时间:然后你后端返回时加个自定义头:
这样你就能精确控制缓存的有效时间。
---
### 4. 检查响应头是否设置了 Cache-Control
有时候即使你的 Service Worker 写对了,浏览器还是用了它自己的默认缓存策略。你可以通过开发者工具的「Network」面板查看接口的响应头:
如果你看到的是
max-age=31536000,那浏览器会缓存一年,Service Worker 也无能为力。**解决办法:在后端设置正确的缓存控制头。**
---
### 5. 最后别忘了 service worker 的更新机制
如果你修改了 service worker 文件(比如
sw.js),浏览器不会自动激活新的 worker,除非你强制更新。**你可以这样操作:**
- 打开 DevTools,Application -> Service Workers
- 点击 unregister 清除注册
- 刷新页面重新注册
- 或者更新 service worker 文件后,点击 update
---
### 总结一下你应该怎么做:
| 期望效果 | 推荐策略 |
|----------|----------|
| 每次刷新都取最新数据 | 使用
network-first策略 || 更新时缓存,但要定时刷新 | 加
X-Cached-Time头 + TTL 判断 || 保证缓存不会被浏览器自动缓存 | 后端设置
Cache-Control: no-cache|| 确保 service worker 生效 | 手动 unregister + 页面刷新 |
---
如果你还卡在那儿,我建议你打开 DevTools,打开「Application」标签下的「Cache Storage」和「Service Workers」,看看缓存内容和激活状态,基本就能定位是缓存问题还是请求流程问题了。