PWA 的 App Shell 缓存不生效是怎么回事?

书生シ正毅 阅读 23

我按照教程用 Workbox 实现了 App Shell 模式,但离线时页面还是白屏,缓存好像没起作用。

我在 sw.js 里注册了路由和缓存策略,关键代码如下:

workbox.routing.registerRoute(
  ({ request }) => request.mode === 'navigate',
  new workbox.strategies.NetworkFirst({
    cacheName: 'app-shell',
    plugins: [
      new workbox.cacheableResponse.CacheableResponsePlugin({
        statuses: [200],
      }),
    ],
  })
);

也确认了 service worker 安装成功,但刷新后离线访问还是加载失败。是不是 App Shell 的 HTML 没被正确缓存?

我来解答 赞 6 收藏
二维码
手机扫码查看
2 条解答
UE丶珍珍
你这代码没缓存 HTML 文件本身,只缓存了 navigate 请求的响应,但 Workbox 默认不会缓存 HTML,得显式指定用 CacheFirst 或 NetworkFirst 并确保 HTML 被预缓存。试试看把 index.html 加进 workbox.precaching.precacheAndRoute,或者改成用 StaleWhileRevalidate 策略并配合 cacheName 显式缓存 HTML,比如:

workbox.precaching.precacheAndRoute([
{ url: '/', revision: '1' },
{ url: '/index.html', revision: '1' }
]);

workbox.routing.registerRoute(
({ request }) => request.mode === 'navigate',
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'app-shell',
plugins: [
new workbox.cacheableResponse.CacheableResponsePlugin({
statuses: [200],
}),
],
})
);
点赞 1
2026-02-27 15:10
春凤
春凤 Lv1
你这个配置看起来没问题,但问题很可能出在 Workbox 的默认行为上——它只缓存 HTML 页面,不缓存这个页面依赖的 JS CSS 图片等资源,所以离线时虽然 HTML 能加载出来,但一执行 JS 就白屏了。

先确认两点:
1. 你的 index.html 本身能不能离线打开?打开浏览器开发者工具的 Application → Service Workers,看有没有命中缓存的记录;
2. 打开 Application → Cache Storage → app-shell,看看里面是不是只有 index.html,没有其他静态资源?

如果只有 HTML,那你要加一条规则,把静态资源也缓存了,比如 JS CSS 文件:

workbox.routing.registerRoute(
({ request }) => request.destination === 'style' || request.destination === 'script',
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'static-resources',
})
);

或者更稳妥点,直接用 Workbox 的默认策略模板,比如:

workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);

这个 manifest 是构建时生成的,包含所有静态资源。如果你没用构建工具(比如 webpack + workbox 插件),那得自己列个清单,或者至少把 JS CSS 图片都加个路由缓存。

另外检查一下 service worker 是否真的控制了页面——在 sw.js 里加一句:

self.addEventListener('install', event => {
console.log('SW installed');
});

在页面里加:

if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(() => console.log('SW ready'));
}

看看控制台有没有输出。有时候 sw.js 没生效是因为路径不对,比如你把 sw.js 放在 /sw.js,但页面在 /app/,那 scope 就是 /,但页面可能加载不了。

复制过去试试这个完整版:

if (workbox) {
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST || []);

workbox.routing.registerRoute(
({ request }) => request.mode === 'navigate',
new workbox.strategies.NetworkFirst({
cacheName: 'app-shell',
plugins: [
new workbox.cacheableResponse.CacheableResponsePlugin({
statuses: [200],
}),
],
})
);

workbox.routing.registerRoute(
({ request }) => request.destination === 'style' || request.destination === 'script',
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'static-resources',
})
);
}

记得用 npm run build 生成 manifest 后再部署,别直接传个 sw.js 就完事。
点赞 1
2026-02-24 11:05