关于 workbox 的实践总结
以下仅是实践总结,如有更好的建议,请留言。
package.json
编译分为两部分(以测试环境为例):
- build 出测试包
- 创建sw.js文件
{ "scripts": { "test": "vue-cli-service build --mode test", "build:pwa": "node ./workbox-build/workbox-build-inject.js", "build:test:web": "npm run test && npm run build:pwa" } } 复制代码
编写 sw.js 文件
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.1.1/workbox-sw.js')
workbox.loadModule('workbox-precaching')
workbox.loadModule('workbox-routing')
workbox.loadModule('workbox-strategies')
workbox.loadModule('workbox-expiration')
const { cleanupOutdatedCaches, precacheAndRoute } = workbox.precaching
const { registerRoute } = workbox.routing
const { CacheFirst, StaleWhileRevalidate } = workbox.strategies
const { ExpirationPlugin } = workbox.expiration
const DAY_IN_SECONDS = 24 * 60 * 60
const MONTH_IN_SECONDS = DAY_IN_SECONDS * 30
// precache
cleanupOutdatedCaches()
const assetsToCache = self.__WB_MANIFEST
precacheAndRoute(assetsToCache)
// routes
registerRoute(/\.(?:js|css)$/, new StaleWhileRevalidate())
registerRoute(
/\.(?:png|gif|jpg|jpeg|svg)$/,
new CacheFirst({
cacheName: 'images-cache',
plugins: [
new ExpirationPlugin({
maxEntries: 250,
maxAgeSeconds: MONTH_IN_SECONDS
})
]
})
)
self.addEventListener('message', (event) => {
if (event && event.data) {
console.debug(`Skipping waiting...`, event.data)
if (event.data === 'SKIP_WAITING') {
console.debug(`Skipping waiting...`)
self.skipWaiting()
self.clients.claim()
}
}
})
复制代码
关于 workbox-build 目录
workbox-build/workbox-build-inject.js
const { injectManifest } = require('workbox-build')
const workboxConfig = require('./workbox-config')
injectManifest(workboxConfig).then(({ count, size }) => {
console.log(`Generated ${workboxConfig.swDest}, which will precache ${count} files (${size} bytes)`)
})
复制代码
workbox-build/workbox-config.js
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = {
swSrc: resolve('../sw.js'),
swDest: resolve('../dist/sw.js'),
globDirectory: resolve('../dist'),
globPatterns: ['vendor/*.js']
}
复制代码
总结实践
- 对于不常变动的资源(例如第三方依赖 vue、vuex、lodash、axios等)创建一个 vendor 文件,作为 precache 缓存(在 service-worker 的 install 阶段缓存)对于匹配到的缓存资源,直接从缓存中读取。
- precache 的缓存通过 workbox-config.js 配置,在 sw.js 中通过占位符 self.__WB_MANIFEST 生成。
- 离线允许 html 可以访问
const defaultRouteHandler = createHandlerBoundToURL('/index.html')
const defaultNavigationRoute = new NavigationRoute(defaultRouteHandler, {
// allowlist: [],
// denylist: [],
})
registerRoute(defaultNavigationRoute)
复制代码
配合 workbox-config.js 中的配置
globPatterns: ['vendor/*.js', '**/*.html']
复制代码
但是要注意,如果缓存 html,sw 激活后需要再次刷新页面才可以更新,因为缓存的 html 引入的是旧的资源,不缓存,直接请求新的文件,再将文件缓存(stale-while-revalidate, falling back to the network request if it’s not cached)
- 对于 js、css 缓存策略是 stale-while-revalidate,如果缓存中有,尽可能快的用缓存响应,然后再用网络请求更新缓存;如果没有缓存,回退到网络请求。这种策略能保证用户尽可能快的得到响应,并且尽可能早地最新文件。
- 对于 .png、.gif、.jpg 等图片资源,使用缓存优先的策略,并配合设置有效期。