最近在开发一个简单的个人抽奖的网站,技术栈是使用 Vite + Vue3+ts,由于使用的单台服务器,有时候服务器会被限制带宽,所以平时都会比较访问比较慢。
所以想实现一个离线应用,而 PWA 应用则是目前最佳方案。
1. 依赖安装
项目使用了以下 PWA 相关依赖:
pnpm add -D vite-plugin-pwa workbox-window
2. 配置文件说明
2.1 Vite 配置 (vite.config.ts)
import { VitePWA } from 'vite-plugin-pwa'
export default defineConfig({
plugins: [
VitePWA({
registerType: 'autoUpdate', // 自动更新
includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'mask-icon.svg'],
manifest: {
name: '积分抽奖',
short_name: '积分抽奖',
description: '积分抽奖系统',
theme_color: '#ffffff',
start_url: '/',
display: 'fullscreen', // 全屏显示
background_color: '#ffffff',
orientation: 'portrait', // 竖屏显示
icons: [
{
src: '/icon.png',
sizes: '200x200',
type: 'image/png'
}
]
},
workbox: {
cleanupOutdatedCaches: true, // 清理过期缓存
globPatterns: ['**/*.{js,css,html,ico,png,svg}'], // 缓存文件类型
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.*/i, // API 缓存策略
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 7 // 7天缓存期限
},
cacheableResponse: {
statuses: [0, 200]
}
}
}
]
},
devOptions: {
enabled: true, // 开发环境启用
type: 'module'
}
})
]
})
2.2 HTML 配置 (index.html)
<head>
<!-- 基础配置 -->
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico">
<!-- 视口配置 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<meta name="description" content="积分抽奖系统">
<meta name="theme-color" content="#ffffff">
<!-- iOS 设备特殊配置 -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="积分抽奖">
<!-- 功能限制 -->
<meta name="format-detection" content="telephone=no,email=no">
<!-- PWA 相关链接 -->
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180">
<link rel="mask-icon" href="/mask-icon.svg" color="#FFFFFF">
<link rel="manifest" href="/manifest.json">
</head>
2.3 PWA 注册 (src/pwa.ts)
import { registerSW } from 'virtual:pwa-register'
const updateSW = registerSW({
onNeedRefresh() {
// 显示更新提示
if (confirm('新版本已经准备就绪,是否更新?')) {
updateSW(true)
}
},
onOfflineReady() {
console.log('应用已经可以离线使用')
},
immediate: true
})
2.4 样式配置 (src/assets/styles/main.scss)
:root {
// 安全区域变量
--safe-area-inset-top: env(safe-area-inset-top);
--safe-area-inset-right: env(safe-area-inset-right);
--safe-area-inset-bottom: env(safe-area-inset-bottom);
--safe-area-inset-left: env(safe-area-inset-left);
}
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
-webkit-overflow-scrolling: touch;
-webkit-tap-highlight-color: transparent;
user-select: none;
}
#app {
// 全屏应用布局
width: 100%;
height: 100%;
overflow: hidden;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
// 处理安全区域
padding-top: var(--safe-area-inset-top);
padding-right: var(--safe-area-inset-right);
padding-bottom: var(--safe-area-inset-bottom);
padding-left: var(--safe-area-inset-left);
}
* {
-webkit-tap-highlight-color: transparent;
}
3. 功能特性
- 全屏显示,隐藏浏览器导航栏
- 离线缓存支持
- 自动更新检测
- API 请求缓存策略
- iOS 设备优化
- 安全区域适配
- 禁用缩放和选择
- 禁用电话和邮箱识别
4. 验证方法
- PWA 安装测试:
- 访问应用
- 查看浏览器地址栏是否出现安装图标
- 点击安装,验证是否可以作为独立应用运行
- 离线功能测试:
- 在 Chrome DevTools 中启用离线模式
- 刷新页面,验证应用是否仍能正常访问
- 更新机制测试:
- 修改应用内容
- 重新部署
- 验证是否收到更新提示
- 缓存验证:
- 打开 Chrome DevTools
- 检查 Application > Cache Storage
- 验证资源是否正确缓存
5. 注意事项
- 确保 /public 目录下存在所需的图标文件:
- favicon.ico
- apple-touch-icon.png
- mask-icon.svg
- icon.png (200x200)
- 在生产环境中必须使用 HTTPS
- 首次访问可能需要用户授权通知权限
- iOS 设备上的体验可能与 Android 设备略有不同