# Service Worker 框架无关的缓存与激活机制
Table of Contents
Service Worker 是 Web 标准技术,完全独立于任何前端框架。它可以在 Vue、React、Angular 或原生 JavaScript 应用中工作。本文将展示在不同框架中如何实现相同的 Service Worker 功能。
Service Worker 的框架无关性
Service Worker 是浏览器原生 API,不依赖于任何框架:
// 在任何框架中都可以使用相同的注册代码if ('serviceWorker' in navigator) { navigator.serviceWorker .register('/sw.js') .then((registration) => console.log('SW registered')) .catch((error) => console.log('SW registration failed'))}
不同框架中的实现
1. Vue 应用中的实现
// main.js (Vue 3)import { createApp } from 'vue'import App from './App.vue'
const app = createApp(App)app.mount('#app')
// 注册 Service Workerif ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js') })}
// 在 Vue 组件中使用export default { mounted() { this.checkForUpdates() }, methods: { checkForUpdates() { if ('serviceWorker' in navigator) { navigator.serviceWorker.getRegistration().then((registration) => { if (registration) { registration.addEventListener('updatefound', () => { // 处理更新 }) } }) } }, },}
2. React 应用中的实现
// index.js (React)import React from 'react'import ReactDOM from 'react-dom'import App from './App'
ReactDOM.render(<App />, document.getElementById('root'))
// 注册 Service Workerif ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js') })}
// React Hook 示例import { useEffect } from 'react'
function useServiceWorker() { useEffect(() => { if ('serviceWorker' in navigator) { navigator.serviceWorker.getRegistration().then((registration) => { if (registration) { registration.addEventListener('updatefound', () => { const newWorker = registration.installing newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { // 显示更新提示 if (confirm('新版本可用,是否刷新?')) { window.location.reload() } } }) }) } }) } }, [])}
// 在组件中使用function App() { useServiceWorker()
return <div>Your app content</div>}
3. Angular 应用中的实现
// main.ts (Angular)import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'import { AppModule } from './app/app.module'
platformBrowserDynamic().bootstrapModule(AppModule)
// 注册 Service Workerif ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js') })}
// Angular Serviceimport { Injectable } from '@angular/core'
@Injectable({ providedIn: 'root',})export class ServiceWorkerService { checkForUpdates() { if ('serviceWorker' in navigator) { navigator.serviceWorker.getRegistration().then((registration) => { if (registration) { registration.addEventListener('updatefound', () => { // 处理更新逻辑 }) } }) } }}
4. 原生 JavaScript 应用
// 原生 JavaScript 实现class ServiceWorkerManager { constructor() { this.init() }
async init() { if ('serviceWorker' in navigator) { try { const registration = await navigator.serviceWorker.register('/sw.js') this.setupUpdateListener(registration) } catch (error) { console.error('SW registration failed:', error) } } }
setupUpdateListener(registration) { registration.addEventListener('updatefound', () => { const newWorker = registration.installing newWorker.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { this.showUpdateNotification() } }) }) }
showUpdateNotification() { if (confirm('新版本可用,是否刷新页面?')) { window.location.reload() } }}
// 使用new ServiceWorkerManager()
通用的 Service Worker 文件
无论使用什么框架,Service Worker 文件本身都是相同的:
// public/sw.js - 适用于所有框架const CACHE_NAME = 'app-cache-v1'const STATIC_CACHE = 'static-v1'const DYNAMIC_CACHE = 'dynamic-v1'
// 安装阶段self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => { return cache.addAll([ '/', '/index.html', '/static/js/bundle.js', '/static/css/main.css', ]) }), )})
// 激活阶段self.addEventListener('activate', (event) => { event.waitUntil( Promise.all([ self.clients.claim(), caches.keys().then((cacheNames) => { return Promise.all( cacheNames.map((cacheName) => { if (![CACHE_NAME, STATIC_CACHE, DYNAMIC_CACHE].includes(cacheName)) { return caches.delete(cacheName) } }), ) }), ]), )})
// 拦截请求self.addEventListener('fetch', (event) => { const { request } = event
// API 请求 - 网络优先 if (request.url.includes('/api/')) { event.respondWith( fetch(request) .then((response) => { const responseClone = response.clone() caches.open(DYNAMIC_CACHE).then((cache) => cache.put(request, responseClone)) return response }) .catch(() => caches.match(request)), ) }
// 静态资源 - 缓存优先 else if ( request.destination === 'script' || request.destination === 'style' || request.destination === 'image' ) { event.respondWith( caches.match(request).then((response) => response || fetch(request)), ) }
// 其他请求 - 网络优先 else { event.respondWith(fetch(request)) }})
框架特定的工具和插件
Vue CLI PWA 插件
# Vue 项目vue add pwa
module.exports = { pwa: { workboxPluginMode: 'GenerateSW', workboxOptions: { skipWaiting: true, clientsClaim: true, }, },}
Create React App PWA
# React 项目npx create-react-app my-app --template cra-template-pwa
import * as serviceWorkerRegistration from './serviceWorkerRegistration'
serviceWorkerRegistration.register()
Angular PWA
# Angular 项目ng add @angular/pwa
import { ServiceWorkerModule } from '@angular/service-worker'
@NgModule({ imports: [ ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }) ]})
通用最佳实践
1. 版本管理
// 所有框架通用的版本管理const APP_VERSION = '1.0.0'const CACHE_NAME = `app-cache-${APP_VERSION}`
// 在应用代码中检查版本async function checkAppVersion() { const response = await fetch('/version.json') const { version } = await response.json()
if (version !== APP_VERSION) { // 提示用户刷新 showUpdateNotification() }}
2. 离线处理
// 通用的离线页面处理self.addEventListener('fetch', (event) => { event.respondWith( caches .match(event.request) .then((response) => response || fetch(event.request)) .catch(() => { if (event.request.destination === 'document') { return caches.match('/offline.html') } }), )})
3. 消息通信
// Service Worker 中self.addEventListener('message', (event) => { if (event.data.type === 'SKIP_WAITING') { self.skipWaiting() }})
// 应用代码中(适用于所有框架)function skipWaiting() { if (navigator.serviceWorker.controller) { navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING', }) }}
调试技巧
Chrome DevTools
// 在所有框架中都可以使用的调试代码if (process.env.NODE_ENV === 'development') { // 开发环境下的调试 navigator.serviceWorker.addEventListener('message', (event) => { console.log('SW Message:', event.data) })}
状态检查
// 通用的状态检查函数async function getServiceWorkerStatus() { if ('serviceWorker' in navigator) { const registration = await navigator.serviceWorker.getRegistration() if (registration) { console.log('SW State:', registration.active?.state) console.log('SW Script URL:', registration.active?.scriptURL) } }}
总结
Service Worker 的框架无关性使其成为构建 PWA 的理想选择:
- 标准化: 基于 Web 标准,不依赖特定框架
- 兼容性: 在所有现代浏览器中工作
- 灵活性: 可以与任何前端框架结合使用
- 工具支持: 各框架都有相应的工具和插件
无论你使用 Vue、React、Angular 还是原生 JavaScript,Service Worker 的核心概念和实现方式都是相同的。框架只是提供了不同的集成方式和开发体验。