阅读 281

service worker 关于 workbox 的最佳实践

关于 workbox 的实践总结

以下仅是实践总结,如有更好的建议,请留言。

package.json

编译分为两部分(以测试环境为例):

  1. build 出测试包
  2. 创建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']
}
复制代码

总结实践

  1. 对于不常变动的资源(例如第三方依赖 vue、vuex、lodash、axios等)创建一个 vendor 文件,作为 precache 缓存(在 service-worker 的 install 阶段缓存)对于匹配到的缓存资源,直接从缓存中读取。
  2. precache 的缓存通过 workbox-config.js 配置,在 sw.js 中通过占位符 self.__WB_MANIFEST 生成。
  3. 离线允许 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)

  1. 对于 js、css 缓存策略是 stale-while-revalidate,如果缓存中有,尽可能快的用缓存响应,然后再用网络请求更新缓存;如果没有缓存,回退到网络请求。这种策略能保证用户尽可能快的得到响应,并且尽可能早地最新文件。
  2. 对于 .png、.gif、.jpg 等图片资源,使用缓存优先的策略,并配合设置有效期。
文章分类
前端
文章标签