PWA 的开发需要使用一系列工具和框架来提高开发效率和代码质量。以下是常用的 PWA 开发工具和框架:
核心开发工具
1. Workbox
Workbox 是 Google 提供的 PWA 开发工具集,简化了 Service Worker 的开发。
安装
bashnpm install workbox-cli --global npm install workbox-webpack-plugin --save-dev
使用 Workbox CLI
bash# 生成 Service Worker workbox generateSW workbox-config.js # 预缓存文件 workbox wizard
Workbox 配置
javascript// workbox-config.js module.exports = { globDirectory: 'dist/', globPatterns: [ '**/*.{html,js,css,png,jpg,jpeg,svg,woff,woff2}' ], swDest: 'dist/sw.js', runtimeCaching: [ { urlPattern: /^https:\/\/api\.example\.com\/.*/, handler: 'NetworkFirst', options: { cacheName: 'api-cache', expiration: { maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 // 30 days } } }, { urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/, handler: 'CacheFirst', options: { cacheName: 'image-cache', expiration: { maxEntries: 60, maxAgeSeconds: 60 * 24 * 60 * 60 // 60 days } } } ] };
在 Webpack 中使用 Workbox
javascript// webpack.config.js const { GenerateSW } = require('workbox-webpack-plugin'); module.exports = { plugins: [ new GenerateSW({ clientsClaim: true, skipWaiting: true, runtimeCaching: [ { urlPattern: /\.(?:png|jpg|jpeg|svg)$/, handler: 'CacheFirst', options: { cacheName: 'images', expiration: { maxEntries: 60 } } } ] }) ] };
2. PWA Builder
PWA Builder 是 Microsoft 提供的工具,可以将 PWA 打包为原生应用。
bash# 安装 PWA Builder CLI npm install -g pwabuilder # 打包应用 pwabuilder package
3. Lighthouse
Lighthouse 是 Google 提供的网站性能和质量审计工具。
bash# 安装 Lighthouse npm install -g lighthouse # 运行审计 lighthouse https://your-pwa.com --view
使用 Lighthouse CI
bash# 安装 Lighthouse CI npm install -g @lhci/cli # 初始化配置 lhci autorun # 运行 CI lhci autorun --collect.url=https://your-pwa.com
框架和库
1. React PWA
Create React App
bash# 创建 PWA 项目 npx create-react-app my-pwa --template cra-template-pwa
使用 React PWA
javascript// src/serviceWorkerRegistration.js export function register(config) { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); if (publicUrl.origin !== window.location.origin) return; window.addEventListener('load', () => { const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; registerValidSW(swUrl, config); }); } }
2. Vue PWA
Vue CLI PWA 插件
bash# 创建 PWA 项目 vue create my-pwa # 选择 PWA 插件
配置 vue.config.js
javascript// vue.config.js module.exports = { pwa: { name: 'My PWA', themeColor: '#4DBA87', msTileColor: '#000000', appleMobileWebAppCapable: 'yes', appleMobileWebAppStatusBarStyle: 'black', workboxPluginMode: 'GenerateSW', workboxOptions: { runtimeCaching: [ { urlPattern: /\.(?:png|jpg|jpeg|svg)$/, handler: 'CacheFirst', options: { cacheName: 'images', expiration: { maxEntries: 60 } } } ] } } };
3. Angular PWA
bash# 添加 PWA 支持 ng add @angular/pwa
配置 ngsw-config.json
json{ "$schema": "./node_modules/@angular/service-worker/config/schema.json", "index": "/index.html", "assetGroups": [ { "name": "app", "installMode": "prefetch", "resources": { "files": [ "/favicon.ico", "/index.html", "/manifest.webmanifest", "/*.css", "/*.js" ] } }, { "name": "assets", "installMode": "lazy", "updateMode": "prefetch", "resources": { "files": [ "/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)" ] } } ], "dataGroups": [ { "name": "api-freshness", "urls": [ "/api/**" ], "cacheConfig": { "maxSize": 100, "maxAge": "3d", "timeout": "10s", "strategy": "freshness" } } ] }
开发工具
1. Chrome DevTools
Service Worker 调试
javascript// 在 DevTools Console 中 // 查看所有 Service Worker navigator.serviceWorker.getRegistrations().then(registrations => { registrations.forEach(registration => { console.log('SW:', registration); }); }); // 取消注册 Service Worker navigator.serviceWorker.getRegistrations().then(registrations => { registrations.forEach(registration => { registration.unregister(); }); });
Application 面板
- Service Workers:查看和管理 Service Worker
- Cache Storage:查看和管理缓存
- Manifest:查看和验证 Manifest 文件
- Background Services:查看后台服务状态
2. React DevTools
bash# 安装 React DevTools npm install --save-dev react-devtools
3. Vue DevTools
bash# 安装 Vue DevTools npm install --save-dev @vue/devtools
测试工具
1. Jest
javascript// sw.test.js describe('Service Worker', () => { beforeEach(() => { return navigator.serviceWorker.register('/sw.js'); }); test('should cache static assets', async () => { const cache = await caches.open('static-cache'); const response = await cache.match('/styles/main.css'); expect(response).toBeDefined(); }); });
2. Cypress
javascript// cypress/integration/pwa.spec.js describe('PWA', () => { it('should work offline', () => { cy.visit('/'); // 模拟离线 cy.window().then((win) => { win.navigator.serviceWorker.controller.postMessage({ type: 'OFFLINE' }); }); // 验证离线功能 cy.contains('Offline').should('be.visible'); }); });
3. Puppeteer
javascript// test-pwa.js const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); // 检查 Service Worker await page.goto('https://your-pwa.com'); const sw = await page.evaluate(() => { return navigator.serviceWorker.getRegistration(); }); console.log('Service Worker:', sw); await browser.close(); })();
构建工具
1. Webpack
javascript// webpack.config.js const { InjectManifest } = require('workbox-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const WebpackPwaManifest = require('webpack-pwa-manifest'); module.exports = { plugins: [ new CopyWebpackPlugin({ patterns: [ { from: 'public/manifest.json', to: 'manifest.json' } ] }), new WebpackPwaManifest({ name: 'My PWA', short_name: 'MyPWA', description: 'My Progressive Web App', background_color: '#ffffff', theme_color: '#4DBA87', icons: [ { src: 'src/assets/icon.png', sizes: [96, 128, 192, 256, 384, 512], destination: 'icons' } ] }), new InjectManifest({ swSrc: './src/sw.js', swDest: 'sw.js' }) ] };
2. Vite
javascript// vite.config.js import { VitePWA } from 'vite-plugin-pwa'; export default { plugins: [ VitePWA({ registerType: 'autoUpdate', includeAssets: ['favicon.ico', 'apple-touch-icon.png'], manifest: { name: 'My PWA', short_name: 'MyPWA', description: 'My Progressive Web App', theme_color: '#ffffff', icons: [ { src: 'pwa-192x192.png', sizes: '192x192', type: 'image/png' }, { src: 'pwa-512x512.png', sizes: '512x512', type: 'image/png' } ] }, workbox: { runtimeCaching: [ { urlPattern: /^https:\/\/api\.example\.com\/.*/, handler: 'NetworkFirst', options: { cacheName: 'api-cache', expiration: { maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 } } } ] } }) ] };
部署工具
1. Netlify
toml# netlify.toml [[headers]] for = "/*" [headers.values] X-Frame-Options = "DENY" X-XSS-Protection = "1; mode=block" Content-Security-Policy = "default-src 'self'" [[redirects]] from = "/*" to = "/index.html" status = 200
2. Vercel
json// vercel.json { "headers": [ { "source": "/(.*)", "headers": [ { "key": "X-Frame-Options", "value": "DENY" }, { "key": "X-XSS-Protection", "value": "1; mode=block" } ] } ], "rewrites": [ { "source": "/(.*)", "destination": "/index.html" } ] }
3. Firebase Hosting
json// firebase.json { "hosting": { "public": "dist", "headers": [ { "source": "**/*.@(eot|otf|ttf|ttc|woff|font.css)", "headers": [ { "key": "Cache-Control", "value": "public, max-age=31536000" } ] } ], "rewrites": [ { "source": "**", "destination": "/index.html" } ] } }
监控和分析
1. Google Analytics
javascript// 在 Service Worker 中跟踪 self.addEventListener('fetch', event => { if (navigator.sendBeacon) { navigator.sendBeacon('/analytics', JSON.stringify({ url: event.request.url, timestamp: Date.now() })); } });
2. Sentry
javascript// 在 Service Worker 中捕获错误 self.addEventListener('error', event => { Sentry.captureException(event.error); }); self.addEventListener('unhandledrejection', event => { Sentry.captureException(event.reason); });
最佳实践
- 使用 Workbox:简化 Service Worker 开发
- 自动化测试:使用 Jest、Cypress 等工具
- 性能监控:使用 Lighthouse 定期审计
- 错误追踪:使用 Sentry 等工具
- CI/CD 集成:自动化构建和部署
- 代码分割:使用 Webpack、Vite 等工具
- 缓存策略:根据资源类型选择合适的策略
- 渐进增强:确保在不支持 PWA 的浏览器中正常工作