偶然间接触到了pwa相关的技术,简单的技术选型之后决定使用vite社区提供的vite-plugin-pwa,相对直接用workbox来说更加的简单明了(基于此进行的二次封装)。
什么是PWA
渐进式 web 应用(PWA)通过结合 Web 和移动应用的特点,为用户带来更加流畅和快速的体验。且 PWA 支持离线访问能力(访问静态资源本地缓存),极大提高了用户交互的流畅性,降低非必要的网络依赖。尤其适合在手机端创建。
当前是基于 Vite 的基础上使用 vite-plugin-pwa 实现 A2HS (Add To Home Screen)和 workbox 离线缓存功能。
基本使用
install
yarn add vite-plugin-pwa
引入(dev.config.ts) 直接放入vite.config.ts都行
import { VitePWA } from 'vite-plugin-pwa'
import { defineConfig } from 'vite'
// 缓存分配逻辑
const getCache = ({ name, pattern }: any) => ({
urlPattern: pattern,
handler: 'CacheFirst' as const,
options: {
cacheName: name,
expiration: {
maxEntries: 500,
maxAgeSeconds: 60 * 60 * 24 * 365 * 2 // 2 years
},
cacheableResponse: {
statuses: [200]
}
}
})
export default defineConfig(({ command }) => {
const isServe = command === 'serve'
return {
build: {
sourcemap: true
},
plugins: [
VitePWA({
registerType: 'autoUpdate', // 注册类型配置
devOptions: {
enabled: true // 开发选项配置,启用插件
//type: 'module'
},
manifest: {
name: 'App', // 请求弹窗内name显示
short_name: 'v-pwa', // 正在添加[name]... 和 桌面显示name
description: '一个Vite PWA测试APP',
theme_color: '#fafafa',
icons: [
{
src: '/face_192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: '/face_512.png',
sizes: '512x512',
type: 'image/png'
}
],
shortcuts: [
{
name: 'docs', // 快捷方式名称
description: 'docs', // 快捷方式描述
url: '/docs/docsIndex', // 快捷方式链接地址
}
]
},
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,jpg,svg}'], //缓存相关静态资源
runtimeCaching: [
// 配置自定义运行时缓存
//getCache({
// pattern: /^https:\/\/enjoytoday.cn\/wp-uploads/,
// name: 'local-upload'
//}),
getCache({
pattern: /^https:\/\/baidu.cn/,
name: 'webapp'
})
]
}
})
]
}
})
使用
- 到这里其实就已经可以在chrome浏览器的地址栏右侧上看到一个可以打开的icon标识了
但这个仅限于是手动主动的打开,如果想要从UI页面引导用户打开的话还需要做一些处理(移动端必须)
- 监听当前浏览器是否支持A2HS,支持则会回调返回event,我们需要对此做一个保存,方便后期调用请求弹窗
window.addEventListener('beforeinstallprompt', (e) => {
// 取消该事件
e.preventDefault()
// 保存event
window.deferredPrompt = e
})
- 保存之后就可以根据需求去引导用户打开是否安装应用的弹窗
// 若浏览器支持,则这里会出发安装操作
const handleGoOpenWin = () => {
// 是否支持此事件(chrome)
if ('BeforeInstallPromptEvent' in window) {
console.log('Chrome-style PWA install experience supported!')
}
if ('standalone' in navigator) {
// only available on iOS - but also on alternative browsers on iOS without Add-to-Homescreen support
console.log('iOS Safari-style PWA Add-to-Homescreen maybe supported!')
}
try {
// 拉起请求弹窗
window.deferredPrompt.prompt()
window.deferredPrompt.userChoice.then((choiceResult) => {
// 点击同意,进行安装
if (choiceResult.outcome === 'accepted') {
console.log('acceptttttt!!!!!')
window.localStorage.setItem('isAcceptPWA', '1')
window.deferredPrompt = null
} else {
console.log('User dismissed the A2HS prompt')
}
})
} catch (e) {
console.log(e, 'eee')
}
}
事件按需求进行任意触发。
- 请求弹窗
注意点(坑)
- 在config中配置manifest时,在icons内,设置图标必须设置192及以上大小的图标,且必须跟图标实际大小一致,最好设置一个192192,一个512512的
- 跟上一点一个位置,icon内设置图标时最好选择png格式,且类型必须和type一致(都是血与泪..)
- config的manifest中,theme_color值必须与入口文件(index.html)中的meta的theme-color一致
目前发现的一些问题
- 安卓有时候添加会非常的耗时(慢)(国内环境缘故)
- 安装流程必须是用户在当前页面有交互之后才可以进行,否则无效
扩展
自定义应用展示内容
有些需求是想要生成的应用内地址改动,不止显示原有的链接内容(而且地址栏参数也会消失)
这里就需要使用iframe占据全部的视图,然后根据是否accept来判断iframe是否进行展示,从而进行对应的操作。
自定义应用打开后的路径(可带参数)
前置要求
- scope内需要标明可使用路径(可以是根)
start_url必须是在scope限定的范围内,否则无效- id跟syart_url一致
- 必须是history路由,hash不行(无法得知#之后的变化)
- 线上内容得在安全域名下(https)才可以正常显示
- 接口返回内容必须为纯JSON内容
动态写入start_url(create)
主要就是根据需求拼接接口内容,随后直接插入<link rel='manifest' src='接口'><link/>