npm i vite-plugin-pwa -D
// vite.config.js / vite.config.ts
import { VitePWA } from 'vite-plugin-pwa'
export default {
plugins: [
VitePWA({
manifest: {
name: 'PWA应用',
short_name: 'PWA',
description: 'PWA应用测试',
theme_color: '#182330',
icons: [ //添加图标, 注意路径和图像像素正确
{
src: '/img/icon/icon_192.png',
sizes: '192x192',
type: 'image/png',
},
]
},
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,jpg,svg}'], //缓存相关静态资源
runtimeCaching: [ // 配置自定义运行时缓存
mode !== 'production'
? {
urlPattern: ({ url }) => url.origin === 'https://app-api-0.com',
handler: 'NetworkFirst',
options: {
cacheName: 'wisbayar-api',
cacheableResponse: {
statuses: [200]
}
}
}
: {
urlPattern: ({ url }) => url.origin === 'https://app-api.id',
handler: 'NetworkFirst',
options: {
cacheName: 'wisbayar-api',
cacheableResponse: {
statuses: [200]
}
}
},
{
urlPattern: /\.(?:png|jpg|jpeg|svg)$/,
handler: 'CacheFirst',
options: {
cacheName: 'wisbayar-images',
expiration: {
// 最多30个图
maxEntries: 30
}
}
},
{
urlPattern: /.*\.js.*/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'wisbayar-js',
expiration: {
maxEntries: 30, // 最多缓存30个,超过的按照LRU原则删除
maxAgeSeconds: 30 * 24 * 60 * 60
},
cacheableResponse: {
statuses: [200]
}
}
},
{
urlPattern: /.*\.css.*/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'wisbayar-css',
expiration: {
maxEntries: 20,
maxAgeSeconds: 30 * 24 * 60 * 60
},
cacheableResponse: {
statuses: [200]
}
}
},
{
urlPattern: /.*\.html.*/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'wisbayar-html',
expiration: {
maxEntries: 20,
maxAgeSeconds: 30 * 24 * 60 * 60
},
cacheableResponse: {
statuses: [200]
}
}
}
]
},
devOptions: {
enabled: true
}
})
]
}
/ main.js
// 在主入口监听PWA注册事件
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
window.deferredPrompt = e;
})
/ App.vue (实际业务页面)
const openAddFlow = () => {
if (isIOS()) {
// 如果是苹果手机,直接显示浏览器设置指引图
showAddTipsDialog.value = true
} else {
try {
window.deferredPrompt.prompt();
window.deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
// showAddToDesktop.value = false
localStorage.setItem('addDesktop',true)
} else {
console.log('User dismissed the A2HS prompt');
}
window.deferredPrompt = null;
});
} catch {
showAddTipsDialog.value = true
}
}
}
icons 不能将size设置的太小,我一开始设置成 64x64,会报如下错误,至少是要144及以上的大小才行,另外如果icon设置大小与实际大小不匹配,也会报错。
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
window.deferredPrompt = e;
})
以上代码的执行时机很重要,最开始我都是放在App.vue的onMounted下执行,发现有时成功有时失败,放到main.js下面执行就每次都能成功,应是在要sw.js执行之前注册完事件才行,所以监听事件执行越早越好。
如果是区域网192.168.0.xx下,你会发现报错 Page is not served form a secure origin
所以测试时只能在 http://localhost:8000/ 这种本地域名测试,手机要测试只能放到带https的测试域名才行