1.下载workbox-v7.4.1本地JS版本:workbox-v7.4.1。
将文件解压到public/static/js文件里面。
2.配置manifest.json,最简能用配置:
{
"name": "citya",
"short_name": "citya",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#ffffff",
"icons": [
{
"src": "/static/img/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/static/img/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
manifest.json文件放在public文件夹里。
html页面引用:
<link rel="manifest" href="/manifest.json">
3.写一个安装app.js,内容如下:
(function () {
'use strict';
if (!('serviceWorker' in navigator)) return;
window.addEventListener('load', function () {
navigator.serviceWorker.register('/sw.js')
.catch(function (err) {
if (window.Sentry) window.Sentry.captureException(err);
});
});
})();
app.js文件放到public/static/js目录中。
4.创建sw.js文件,内容如下:
importScripts('/static/js/workbox-v7.4.1/workbox-sw.js');
const RUNTIME_VERSION = 'v3.0.5';
workbox.setConfig({
modulePathPrefix: '/static/js/workbox-v7.4.1/'
});
/**
* 管理内容不能缓存
*/
self.addEventListener('fetch', (event) => {
if (event.request.destination === 'manifest') return;
const url = new URL(event.request.url);
if (url.pathname.startsWith('/admin')) {
const headers = new Headers(event.request.headers);
headers.set('Cache-Control', 'no-cache, no-store, must-revalidate');
headers.set('Pragma', 'no-cache');
const newRequest = new Request(event.request, { headers });
event.respondWith(fetch(newRequest));
return;
}
});
/**
* 安装后立即激活
*/
self.addEventListener('install', () => {
self.skipWaiting();
});
/**
* 预缓存核心静态资源
*/
workbox.precaching.precacheAndRoute([
{ url: '/offline.html', revision: '1' },
{ url: '/static/css/style.css', revision: '1' },
{ url: '/static/js/app.js', revision: '1' }
]);
/**
* /admin 路径:只走网络(Workbox 层面保险)
*/
workbox.routing.registerRoute(
({ url }) => url.pathname.startsWith('/admin'),
new workbox.strategies.NetworkOnly()
);
/**
* 带参数的请求:不缓存
*/
workbox.routing.registerRoute(
({ url }) => url.search.length > 0,
new workbox.strategies.NetworkOnly()
);
/**
* HTML 页面
*/
workbox.routing.registerRoute(
({ request, url }) => {
return request.mode === 'navigate' && !url.pathname.startsWith('/admin');
},
new workbox.strategies.NetworkFirst({
cacheName: `html-cache-${RUNTIME_VERSION}`,
plugins: [
new workbox.expiration.ExpirationPlugin({
maxEntries: 500,
}),
{
handlerDidError: async () => {
const offlineResp = await workbox.precaching.matchPrecache('/offline.html');
if (offlineResp) return offlineResp;
return new Response('离线,暂无缓存', {
status: 503,
headers: { 'Content‑Type': 'text/html;charset=utf‑8' }
});
}
}
]
})
);
/**
* 图片:先缓存后台更新
*/
workbox.routing.registerRoute(
({ request }) => request.destination === 'image',
new workbox.strategies.StaleWhileRevalidate({
cacheName: `image-cache-${RUNTIME_VERSION}`,
plugins: [
new workbox.expiration.ExpirationPlugin({
maxEntries: 500,
})
]
})
);
/**
* JS / CSS / 字体:永久缓存
*/
workbox.routing.registerRoute(
({ request }) =>
request.destination === 'script' ||
request.destination === 'style' ||
request.destination === 'font',
new workbox.strategies.StaleWhileRevalidate({
cacheName: `static-cache-${RUNTIME_VERSION}`,
plugins: [
new workbox.expiration.ExpirationPlugin({
maxEntries: 500,
}),
new workbox.cacheableResponse.CacheableResponsePlugin({
statuses: [0, 200]
})
]
})
);
/**
* POST/PUT/DELETE/PATCH:只走网络
*/
workbox.routing.registerRoute(
({ request }) => ['POST', 'PUT', 'DELETE', 'PATCH'].includes(request.method),
new workbox.strategies.NetworkOnly()
);
/**
* API 只能通过网络
*/
workbox.routing.registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new workbox.strategies.NetworkOnly()
);
/**
* 激活时清理旧缓存
*/
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((name) => {
// 清理 html-cache 里已缓存的 admin 页面
if (name === `html-cache-${RUNTIME_VERSION}`) {
return caches.open(name).then((cache) => {
return cache.keys().then((requests) => {
return Promise.all(
requests
.filter((req) => req.url.includes('/admin'))
.map((req) => cache.delete(req))
);
});
});
}
// 保留 workbox 自身缓存
if (name.startsWith('workbox-')) return Promise.resolve();
// 保留当前版本的缓存
if (name.endsWith(`-${RUNTIME_VERSION}`)) return Promise.resolve();
return caches.delete(name);
})
);
}).then(() => self.clients.claim())
);
});
sw.js文件,放在public目录中。offline.html是用于没有缓存也没有网络的时候显示的提示页面:
5.Html页面引用app.js文件:
<script src="/static/js/app.js" defer>script>
经过上面的配置,网站就有了基本离线功能,只要打开过的网页,在没有网络的情况下也能够打开预览。