Vue 3 与 Workbox 深度集成
概述
Workbox 是 Google 开发的一套用于构建 Progressive Web App (PWA) 的工具库,它简化了 Service Worker 的开发和维护工作。Workbox 提供了多种缓存策略、预缓存机制、路由处理、背景同步等功能,使得开发者能够轻松构建功能完善、性能优良的 PWA 应用。
Vue 3 凭借其现代化的架构和良好的性能,与 Workbox 结合使用时能够发挥出强大的威力。本集将深入探讨 Workbox 的核心功能以及如何在 Vue 3 应用中深度集成 Workbox,包括预缓存配置、运行时缓存策略、背景同步、推送通知等高级功能。
核心知识点
1. Workbox 简介
1.1 什么是 Workbox
Workbox 是一套用于构建 PWA 的 JavaScript 库,它提供了一系列工具和 API,简化了 Service Worker 的开发和维护。Workbox 由 Google Chrome 团队开发,是构建 PWA 的官方推荐工具之一。
1.2 Workbox 核心功能
- 预缓存:在 Service Worker 安装时预缓存指定的资源
- 运行时缓存:根据不同的缓存策略处理运行时的网络请求
- 路由处理:灵活的路由匹配和处理机制
- 背景同步:在后台执行数据同步操作
- 推送通知:处理推送通知事件
- 定期同步:定期执行任务
- 缓存管理:自动管理缓存大小和过期时间
1.3 Workbox 优势
- 简化开发:提供了简单易用的 API,降低了 Service Worker 的开发难度
- 性能优化:内置了多种缓存策略,优化了应用的性能
- 可扩展性:支持插件机制,可以扩展 Workbox 的功能
- 良好的文档和社区支持:拥有完善的文档和活跃的社区
- 持续更新:由 Google Chrome 团队维护,持续更新和改进
2. Vue 3 中集成 Workbox
2.1 使用 Vite PWA 插件集成 Workbox
在 Vue 3 应用中,我们可以使用 vite-plugin-pwa 插件来集成 Workbox。该插件内部使用了 Workbox,提供了简单的配置选项。
首先,安装插件:
npm install vite-plugin-pwa -D然后,在 vite.config.js 中配置插件:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
vue(),
VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'masked-icon.svg'],
manifest: {
name: 'Vue 3 Workbox Deep Integration',
short_name: 'Vue 3 Workbox',
description: 'Vue 3 with Workbox deep integration tutorial',
theme_color: '#42b983',
background_color: '#ffffff',
display: 'standalone',
scope: '/',
start_url: '/',
icons: [
{
src: 'pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: 'pwa-512x512.png',
sizes: '512x512',
type: 'image/png'
},
{
src: 'pwa-maskable-192x192.png',
sizes: '192x192',
type: 'image/png',
purpose: 'maskable'
},
{
src: 'pwa-maskable-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable'
}
]
},
workbox: {
// Workbox 配置
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.example\.com\//,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 // 24 小时
},
cacheableResponse: {
statuses: [0, 200]
}
}
}
]
}
})
]
})2.2 手动集成 Workbox
除了使用 Vite PWA 插件外,我们还可以手动集成 Workbox。首先,安装 Workbox:
npm install workbox-core workbox-precaching workbox-routing workbox-strategies workbox-expiration workbox-cacheable-response -D然后,创建一个 Service Worker 文件:
// public/service-worker.js
import { precacheAndRoute } from 'workbox-precaching'
import { registerRoute } from 'workbox-routing'
import { NetworkFirst, CacheFirst, StaleWhileRevalidate } from 'workbox-strategies'
import { ExpirationPlugin } from 'workbox-expiration'
import { CacheableResponsePlugin } from 'workbox-cacheable-response'
// 预缓存资源
precacheAndRoute(self.__WB_MANIFEST)
// 注册路由
// 静态资源:Cache First 策略
registerRoute(
({ request }) => request.destination === 'style' || request.destination === 'script' || request.destination === 'image',
new CacheFirst({
cacheName: 'static-resources',
plugins: [
new ExpirationPlugin({
maxEntries: 1000,
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 天
})
]
})
)
// API 请求:Network First 策略
registerRoute(
({ url }) => url.origin === 'https://api.example.com',
new NetworkFirst({
cacheName: 'api-cache',
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200]
}),
new ExpirationPlugin({
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 // 24 小时
})
]
})
)
// 其他请求:Stale While Revalidate 策略
registerRoute(
({ request }) => request.method === 'GET',
new StaleWhileRevalidate({
cacheName: 'other-resources',
plugins: [
new ExpirationPlugin({
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24 // 24 小时
})
]
})
)最后,在 vite.config.js 中配置 Workbox:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteSingleFile } from 'vite-plugin-singlefile'
import { generateSW } from 'workbox-build'
// 构建后生成 Service Worker
const workbox = () => {
return {
name: 'workbox',
closeBundle: async () => {
await generateSW({
swDest: './dist/service-worker.js',
globDirectory: './dist',
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.example\.com\//,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24
},
cacheableResponse: {
statuses: [0, 200]
}
}
}
]
})
}
}
}
export default defineConfig({
plugins: [
vue(),
workbox()
]
})3. Workbox 预缓存配置
3.1 预缓存资源
预缓存是 Workbox 的核心功能之一,它允许我们在 Service Worker 安装时预缓存指定的资源。我们可以使用 precacheAndRoute 函数来配置预缓存:
// public/service-worker.js
import { precacheAndRoute } from 'workbox-precaching'
// 预缓存资源
precacheAndRoute([
{
url: '/',
revision: '1.0.0'
},
{
url: '/index.html',
revision: '1.0.0'
},
{
url: '/assets/app.js',
revision: '1.0.0'
},
{
url: '/assets/app.css',
revision: '1.0.0'
},
{
url: '/assets/icon.png',
revision: '1.0.0'
}
])使用 vite-plugin-pwa 插件时,我们可以在 vite.config.js 中配置预缓存:
// vite.config.js
VitePWA({
// ...
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
globIgnores: ['node_modules/**/*', '.git/**/*', 'dist/**/*.map'],
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024 // 5MB
}
})3.2 预缓存策略
Workbox 提供了多种预缓存策略,我们可以根据需要选择合适的策略:
- precacheAndRoute:预缓存资源并为这些资源注册路由
- precache:只预缓存资源,不注册路由
- precacheAndRouteWithNetworkUpdate:预缓存资源并注册路由,同时在网络有更新时更新缓存
3.3 预缓存清单
Workbox 使用预缓存清单来管理预缓存的资源。预缓存清单是一个包含资源 URL 和版本号的数组,Workbox 会根据版本号来判断是否需要更新资源。
在使用 vite-plugin-pwa 插件时,预缓存清单会自动生成,我们不需要手动维护。
4. Workbox 运行时缓存策略
4.1 缓存策略类型
Workbox 提供了多种缓存策略,我们可以根据不同的资源类型选择合适的策略:
- NetworkFirst:优先从网络获取资源,网络不可用时从缓存获取
- CacheFirst:优先从缓存获取资源,缓存中不存在时从网络获取
- StaleWhileRevalidate:先从缓存返回资源(如果存在),然后在后台从网络获取并更新缓存
- NetworkOnly:只从网络获取资源
- CacheOnly:只从缓存获取资源
4.2 配置运行时缓存
我们可以使用 registerRoute 函数来配置运行时缓存:
// public/service-worker.js
import { registerRoute } from 'workbox-routing'
import { NetworkFirst, CacheFirst, StaleWhileRevalidate } from 'workbox-strategies'
import { ExpirationPlugin } from 'workbox-expiration'
import { CacheableResponsePlugin } from 'workbox-cacheable-response'
// API 请求:Network First 策略
registerRoute(
({ url }) => url.origin === 'https://api.example.com',
new NetworkFirst({
cacheName: 'api-cache',
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200]
}),
new ExpirationPlugin({
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 // 24 小时
})
]
})
)
// 静态资源:Cache First 策略
registerRoute(
({ request }) => request.destination === 'style' || request.destination === 'script' || request.destination === 'image',
new CacheFirst({
cacheName: 'static-resources',
plugins: [
new ExpirationPlugin({
maxEntries: 1000,
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 天
})
]
})
)
// 字体资源:Stale While Revalidate 策略
registerRoute(
({ url }) => url.origin === 'https://fonts.googleapis.com' || url.origin === 'https://fonts.gstatic.com',
new StaleWhileRevalidate({
cacheName: 'fonts',
plugins: [
new ExpirationPlugin({
maxEntries: 20,
maxAgeSeconds: 60 * 60 * 24 * 365 // 1 年
})
]
})
)4.3 自定义缓存策略
除了使用内置的缓存策略外,我们还可以自定义缓存策略:
// public/service-worker.js
import { registerRoute } from 'workbox-routing'
import { Strategy } from 'workbox-strategies'
class CustomStrategy extends Strategy {
async _handle(request, handler) {
try {
// 先尝试从网络获取
const networkResponse = await handler.fetch(request)
return networkResponse
} catch (error) {
// 网络请求失败,从缓存获取
const cachedResponse = await handler.cacheMatch(request)
if (cachedResponse) {
return cachedResponse
}
// 缓存中也没有,返回默认响应
return new Response('Network error occurred', {
status: 503,
headers: { 'Content-Type': 'text/plain' }
})
}
}
}
// 注册自定义策略
registerRoute(
({ request }) => request.url.includes('/custom/'),
new CustomStrategy({
cacheName: 'custom-strategy-cache'
})
)5. Workbox 路由处理
5.1 路由匹配
Workbox 提供了灵活的路由匹配机制,我们可以根据 URL、请求方法、请求头等条件来匹配路由:
// public/service-worker.js
import { registerRoute } from 'workbox-routing'
import { NetworkFirst } from 'workbox-strategies'
// 根据 URL 匹配
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new NetworkFirst({ cacheName: 'api-cache' })
)
// 根据请求方法匹配
registerRoute(
({ request }) => request.method === 'GET',
new NetworkFirst({ cacheName: 'get-requests' })
)
// 根据请求头等条件匹配
registerRoute(
({ request }) => request.headers.get('Accept').includes('application/json'),
new NetworkFirst({ cacheName: 'json-requests' })
)
// 根据请求目的地匹配
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({ cacheName: 'images' })
)5.2 自定义路由匹配
我们还可以使用自定义函数来匹配路由:
// public/service-worker.js
import { registerRoute } from 'workbox-routing'
import { NetworkFirst } from 'workbox-strategies'
// 自定义匹配函数
const matchFunction = ({ url, request }) => {
// 匹配所有 /api/ 开头且请求方法为 GET 的请求
return url.pathname.startsWith('/api/') && request.method === 'GET'
}
registerRoute(
matchFunction,
new NetworkFirst({ cacheName: 'custom-match-api-cache' })
)6. Workbox 缓存管理
6.1 缓存过期
Workbox 提供了 ExpirationPlugin 插件来管理缓存的过期时间和最大条目数:
// public/service-worker.js
import { registerRoute } from 'workbox-routing'
import { CacheFirst } from 'workbox-strategies'
import { ExpirationPlugin } from 'workbox-expiration'
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
new ExpirationPlugin({
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 * 30, // 30 天
purgeOnQuotaError: true // 在配额不足时清除缓存
})
]
})
)6.2 缓存大小管理
Workbox 会自动管理缓存大小,当缓存大小超过浏览器的配额时,会触发 QuotaExceededError 错误。我们可以通过以下方式来管理缓存大小:
- 设置合理的
maxEntries和maxAgeSeconds - 使用
purgeOnQuotaError: true选项,在配额不足时清除缓存 - 定期清理不需要的缓存
6.3 清理旧缓存
我们可以使用 workbox-expiration 插件来清理旧缓存:
// public/service-worker.js
import { cleanupOutdatedCaches } from 'workbox-precaching'
// 清理旧缓存
cleanupOutdatedCaches()7. Workbox 背景同步
7.1 配置背景同步
Workbox 提供了 BackgroundSyncPlugin 插件来支持背景同步:
// public/service-worker.js
import { registerRoute } from 'workbox-routing'
import { NetworkOnly } from 'workbox-strategies'
import { BackgroundSyncPlugin } from 'workbox-background-sync'
// 注册背景同步路由
registerRoute(
({ request }) => request.method === 'POST' && request.url.includes('/api/'),
new NetworkOnly({
plugins: [
new BackgroundSyncPlugin('sync-api-requests', {
maxRetentionTime: 24 * 60 // 24 小时
})
]
}),
'POST'
)7.2 处理背景同步事件
当网络恢复后,Workbox 会自动重试失败的请求。我们也可以手动监听背景同步事件:
// public/service-worker.js
self.addEventListener('backgroundsync', (event) => {
if (event.tag === 'sync-api-requests') {
event.waitUntil(
// 处理背景同步逻辑
(async () => {
// 获取所有待同步的请求
const queue = new workbox.backgroundSync.Queue('sync-api-requests')
const entries = await queue.getAll()
// 处理每个请求
for (const entry of entries) {
try {
await fetch(entry.request)
// 请求成功,从队列中删除
await queue.deleteEntry(entry.id)
} catch (error) {
// 请求失败,保留在队列中
console.error('Background sync failed:', error)
}
}
})()
)
}
})8. Workbox 推送通知
8.1 处理推送事件
Workbox 可以帮助我们处理推送通知事件:
// public/service-worker.js
self.addEventListener('push', (event) => {
if (!event.data) return
try {
const data = event.data.json()
const options = {
body: data.body,
icon: '/assets/icon.png',
badge: '/assets/badge.png',
vibrate: [100, 50, 100],
data: {
url: data.url || '/'
}
}
event.waitUntil(
self.registration.showNotification(data.title, options)
)
} catch (error) {
console.error('Failed to process push event:', error)
}
})
// 处理通知点击事件
self.addEventListener('notificationclick', (event) => {
event.notification.close()
const url = event.notification.data.url
event.waitUntil(
clients.openWindow(url)
)
})9. Workbox 高级配置
9.1 自定义 Workbox 配置
我们可以在 vite.config.js 中自定义 Workbox 配置:
// vite.config.js
VitePWA({
// ...
workbox: {
// 预缓存配置
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
globIgnores: ['node_modules/**/*', '.git/**/*', 'dist/**/*.map'],
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
// 运行时缓存配置
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.example\.com\//,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24
},
cacheableResponse: {
statuses: [0, 200]
}
}
}
],
// 其他配置
skipWaiting: true,
clientsClaim: true
}
})9.2 使用 Workbox 插件
Workbox 支持插件机制,我们可以使用内置插件或自定义插件来扩展 Workbox 的功能:
// public/service-worker.js
import { registerRoute } from 'workbox-routing'
import { CacheFirst } from 'workbox-strategies'
import { ExpirationPlugin } from 'workbox-expiration'
import { CacheableResponsePlugin } from 'workbox-cacheable-response'
// 自定义插件
class CustomPlugin {
// 插件方法
requestWillFetch({ request }) {
console.log('Request will fetch:', request.url)
return request
}
cacheWillUpdate({ request, response }) {
console.log('Cache will update:', request.url, response.status)
return response
}
cacheDidUpdate({ request, oldResponse, newResponse }) {
console.log('Cache did update:', request.url)
}
}
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
new ExpirationPlugin({
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 * 30
}),
new CacheableResponsePlugin({
statuses: [0, 200]
}),
new CustomPlugin() // 使用自定义插件
]
})
)最佳实践
1. 合理选择缓存策略
- 静态资源:使用
CacheFirst策略,配合较长的过期时间 - API 请求:使用
NetworkFirst或StaleWhileRevalidate策略 - 频繁更新的资源:使用
NetworkFirst策略 - 不常更新的资源:使用
CacheFirst策略 - 关键资源:使用
NetworkOnly策略,确保获取最新资源
2. 优化预缓存
- 只预缓存必要的资源,避免预缓存过大的资源
- 为预缓存资源设置合理的版本号
- 定期清理旧的预缓存资源
3. 管理缓存大小
- 设置合理的
maxEntries和maxAgeSeconds - 使用
purgeOnQuotaError: true选项 - 定期清理不需要的缓存
4. 实现优雅降级
- 在 Service Worker 不可用时,确保应用仍能正常工作
- 实现适当的错误处理,避免 Service Worker 错误影响应用
5. 监控 Service Worker 性能
- 添加日志记录,便于调试和分析问题
- 使用 Performance API 监控 Service Worker 性能
- 监控缓存命中率,优化缓存策略
6. 测试 Service Worker
- 测试不同网络条件下的应用表现
- 测试 Service Worker 的更新机制
- 测试缓存策略的效果
- 测试背景同步和推送通知功能
常见问题和解决方案
1. 缓存策略导致用户看不到最新内容
问题:用户看到的是旧内容,无法获取最新内容
解决方案:
- 对频繁更新的资源使用
NetworkFirst或StaleWhileRevalidate策略 - 为静态资源添加版本号或哈希值
- 实现 Service Worker 自动更新机制
- 使用
workbox-precaching的cleanupOutdatedCaches方法清理旧缓存
2. 预缓存资源过大
问题:预缓存资源过大,导致 Service Worker 安装失败
解决方案:
- 只预缓存必要的资源
- 对大文件使用运行时缓存,不进行预缓存
- 调整
maximumFileSizeToCacheInBytes选项 - 压缩和优化资源,减少资源大小
3. 背景同步不工作
问题:背景同步无法正常执行
解决方案:
- 确保浏览器支持 Background Sync API
- 检查 Service Worker 中的背景同步配置
- 实现适当的错误处理和重试机制
- 检查网络连接状态
4. 推送通知无法正常工作
问题:推送通知无法发送或接收
解决方案:
- 确保使用 HTTPS 协议
- 检查推送服务配置是否正确
- 确保用户已授予通知权限
- 检查 Service Worker 中的推送事件处理逻辑
5. Service Worker 性能问题
问题:Service Worker 导致应用性能下降
解决方案:
- 减少 Service Worker 脚本体积
- 优化缓存策略,避免不必要的缓存
- 优化路由匹配,避免复杂的匹配逻辑
- 合理使用异步操作,避免阻塞主线程
进阶学习资源
1. 官方文档
2. 教程和博客
- Workbox: A Guide for Beginners
- Caching Strategies in Workbox
- Background Sync with Workbox
- Push Notifications with Workbox
3. 视频资源
4. 开源项目和示例
实践练习
练习 1:Vue 3 集成 Workbox
- 创建一个 Vue 3 应用
- 使用
vite-plugin-pwa插件集成 Workbox - 配置预缓存和运行时缓存
- 测试应用的离线工作能力
- 测试应用的自动更新功能
练习 2:Workbox 缓存策略
- 配置不同资源类型的缓存策略
- 测试不同缓存策略的效果
- 实现自定义缓存策略
- 优化缓存策略,提高应用性能
练习 3:Workbox 背景同步
- 配置 Workbox 背景同步
- 测试背景同步功能
- 实现背景同步的错误处理和重试机制
- 测试不同网络条件下的背景同步表现
练习 4:Workbox 推送通知
- 配置 Workbox 推送通知
- 测试推送通知的订阅和取消订阅
- 测试推送通知的显示和点击处理
- 实现推送通知的高级功能,如通知操作和数据传递
练习 5:Workbox 性能优化
- 优化预缓存配置
- 优化缓存策略
- 实现自定义插件监控 Service Worker 性能
- 测试不同配置下的应用性能
总结
Workbox 是构建 PWA 的强大工具库,它简化了 Service Worker 的开发和维护工作。在 Vue 3 应用中深度集成 Workbox 可以帮助我们构建功能完善、性能优良的 PWA 应用。
在本集中,我们学习了 Workbox 的核心功能以及如何在 Vue 3 应用中深度集成 Workbox。我们探讨了预缓存配置、运行时缓存策略、路由处理、缓存管理、背景同步、推送通知等高级功能,并介绍了相关的最佳实践和常见问题解决方案。
通过掌握 Workbox 的深度集成,你将能够构建更加可靠、高效、用户体验更好的 Vue 3 PWA 应用。Workbox 为 Vue 3 应用提供了强大的后台功能支持,是现代 Web 应用开发的重要工具之一。
下一集我们将学习 Vue 3 与 Web Share API,敬请期待!