前提
确定你的uni-app
支持cli
方式安装其他三方包
安装
在uni-app
根目录执行
~vue add @vue/pwa~
上面那个库不能打包成App
或者其他版本,我修改一下,重新传了一个包
vue add vue-cli-plugin-unipwa
正常情况下,该命令会生成pwa
的插件以及相关依赖包和文件。
- 一些
Vue
的 logo 的图片在public
目录下面
src/registerServiceWorker.js
文件
该文件还会自动在main.js
中进行引入
如果像我一样还要兼容APP
或者其他模式打包的,建议在import
语句外面套上一层,只允许它在H5
模式下进行引入
配置vue.config.js
该文件在项目根目录,没有请自行创建
module.exports = {
pwa: {
manifestOptions: {
"name": "Demo",
"short_name": "Demo",
"theme_color": "#4DBA87",
"icons": [
{
"src": "./static/favicon/icons/icon_x16.png",
"sizes": "16x16",
"type": "image/png"
},
{
"src": "./static/favicon/icons/icon_x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "./static/favicon/icons/icon_x48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "./static/favicon/icons/icon_x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "./static/favicon/icons/icon_x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "./static/favicon/icons/icon_x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "./static/favicon/icons/icon_x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "./static/favicon/icons/icon_x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "./static/favicon/icons/icon_x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "./static/favicon/icons/icon-1024x1024.png",
"sizes": "1024x1024",
"type": "image/png"
},
{
"src": "./static/favicon/icons/maskable_icon_x16.png",
"sizes": "16x16",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./static/favicon/icons/maskable_icon_x32.png",
"sizes": "32x32",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./static/favicon/icons/maskable_icon_x48.png",
"sizes": "48x48",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./static/favicon/icons/maskable_icon_x72.png",
"sizes": "72x72",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./static/favicon/icons/maskable_icon_x96.png",
"sizes": "96x96",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./static/favicon/icons/maskable_icon_x128.png",
"sizes": "128x128",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./static/favicon/icons/maskable_icon_x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./static/favicon/icons/maskable_icon_x384.png",
"sizes": "384x384",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./static/favicon/icons/maskable_icon_x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./static/favicon/icons/maskable_icon.png",
"sizes": "1024x1024",
"type": "image/png",
"purpose": "maskable"
},
],
"start_url": ".",
"display": "standalone",
"background_color": "#000000"
},
name: 'Bogin',
themeColor: '#4DBA87',
msTileColor: '#000000',
appleMobileWebAppCapable: 'yes',
appleMobileWebAppStatusBarStyle: 'black',
iconPaths: {
favicon32: 'static/favicon/icons/icon_x16.png',
favicon16: 'static/favicon/icons/icon_x32.png',
appleTouchIcon: 'static/favicon/icons/apple-touch-icon-152x152.png',
maskIcon: 'img/icons/safari-pinned-tab.svg',
msTileImage: 'static/favicon/icons/msapplication-icon-144x144.png'
},
workboxPluginMode: 'GenerateSW',
}
}
其中manifestOptions
参数,可以修改最终生成的manifest.json
中的一些数据
PS :修改 ICON 实在是太痛苦了,我给他上面的 ICON 写了个自动生成脚本。
使用的是Python
语言来开发脚本,需要修改配置才能使用
from PIL import Image
output_path = ".\\src\\static\\favicon\\icons"
output_size = [
{
"size": 16,
"names": [
"icon_x16",
"maskable_icon_x16"
]
},
{
"size": 32,
"names": [
"icon_x32",
"maskable_icon_x32"
]
},
{
"size": 48,
"names": [
"icon_x48",
"maskable_icon_x48"
]
},
{
"size": 72,
"names": [
"icon_x72",
"maskable_icon_x72"
]
},
{
"size": 96,
"names": [
"icon_x96",
"maskable_icon_x96"
]
},
{
"size": 128,
"names": [
"icon_x128",
"maskable_icon_x128"
]
},
{
"size": 192,
"names": [
"icon_x192",
"maskable_icon_x192"
]
},
{
"size": 384,
"names": [
"icon_x384",
"maskable_icon_x384"
]
},
{
"size": 512,
"names": [
"icon_x512",
"maskable_icon_x512",
"maskable_icon"
]
},
{
"size": 1024,
"names": [
"icon-1024x1024"
]
},
{
"size": 152,
"names": [
"apple-touch-icon-152x152"
]
},
{
"size": 144,
"names": [
"msapplication-icon-144x144.png"
]
}
]
origin_img = Image.open("./src/static/images/logo.png")
for img in output_size:
size = img["size"]
names = img['names']
target = origin_img.resize((size, size),Image.ANTIALIAS)
for name in names:
target.save(output_path + "\\" + name + '.png')
Safari 增加判断提示安装
非常可惜,Safari 并不支持原生的安装提醒,所以我们需要自己写一个提示框
PromptPopup.vue
<template>
<view class="prompt-popup-container" v-if="showInstallMessage">
<view class="prompt-popup-wrapper">
<view class="close-tip-btn">
<u-icon name="close-circle" size="40" class="close-icon" @click="onClick"></u-icon>
</view>
Safari瀏覽器中點擊<image class="propmt-share-icon" src="@/static/favicon/icons/share.png" mode="aspectFill"></image> ,選擇【添加到主熒幕】快速安裝
<view class="triangle"></view>
</view>
</view>
</template>
<script>
import { isIos, isInStandaloneMode } from './showPopup.js';
export default {
data() {
return {
showInstallMessage: false,
}
},
mounted() {
console.log(window.navigator.userAgent)
const status = uni.getStorageSync('isCloseTip')
if (!status) {
this.init();
}
},
methods: {
init() {
console.log(isIos())
if (isIos() && !isInStandaloneMode()) {
this.showInstallMessage = true;
}
},
hide() {
this.showInstallMessage = false;
},
onClick() {
uni.setStorageSync("isCloseTip", "abcdedfs")
this.hide();
}
}
}
</script>
<style lang="less">
.prompt-popup-container {
position: fixed;
bottom: 30rpx;
left: 40rpx;
right: 40rpx;
z-index: 1000;
.prompt-popup-wrapper {
position: relative;
background-color: #E5E5E5;
border-radius: 10rpx;
padding: 10rpx 30rpx 30rpx 30rpx;
.close-tip-btn {
position: relative;
line-height: 30rpx;
height: 30rpx;
.close-icon {
position: absolute;
top: 0;
right: 0;
}
}
.propmt-share-icon {
width: 50rpx;
height: 40rpx;
}
.triangle {
z-index: 999;
border: 24rpx solid transparent;
border-top: 24rpx solid #E5E5E5;
position: absolute;
left: 50%;
bottom: -48rpx;
transform: translateX(-50%);
}
}
}
</style>
showPopup.js
export const isIos = () => {
const userAgent = window.navigator.userAgent.toLowerCase();
return /iphone|ipad|ipod/.test( userAgent );
}
export const isInStandaloneMode = () => {
return ('standalone' in window.navigator) && (window.navigator.standalone)
}
简单的进行一个提示和判断
打包
npm run build
打包以后就是这个样子
- 自动生成了
manifest.json
- 自动注册了
service-worker.js
这块儿就不用多说了,直接部署即可
缓存刷新策略
虽然基本都差不多了,也可以使用了,但是还有一些问题没有解决
- ~ 在 Safari 浏览器上面 logo 经常不显示~
- 缓存刷新策略是,打开网页 → 拉取 service register 文件 (进入 watting 状态) → 然后关闭网页重新打开才会刷新
- ~ 开发过程中遇到的一个新的问题,首次加载时候,PWA 是没有生效的,也就是说,PWA 中的静态文件已经缓存了,比如 JS 文件,但是浏览器还是在从后端获取。直到你下一次刷新或重新打开才会生效,从 service-worker 里面获取数据~
IOS Safari 浏览器兼容问题参考
https://juejin.cn/post/6844903879683866632#heading-12
简单总结一下上面的文章:
Safari 上应用图标兼容:apple-touch-icon:应用图标
<link rel="apple-touch-icon" href="/custom_icon.png">
兼容不同分辨率如下:
<link rel="apple-touch-icon" href="touch-icon-iphone.png">
<link rel="apple-touch-icon" sizes="152x152" href="touch-icon-ipad.png">
<link rel="apple-touch-icon" sizes="180x180" href="touch-icon-iphone-retina.png">
<link rel="apple-touch-icon" sizes="167x167" href="touch-icon-ipad-retina.png">
apple-touch-startup-image:启动画面
<link rel="apple-touch-startup-image" href="/launch.png">
apple-mobile-web-app-title:应用 icon 的标题
默认情况下使用 <title></title>
中的值,需要修改的话需要指定 meta。
<meta name="apple-mobile-web-app-title" content="应用标题">
apple-mobile-web-app-status-bar-style:应用状态栏的外观样式
设置为黑色,这个我没测试,不知道是什么用
<meta name="apple-mobile-web-app-status-bar-style" content="black">
PWA 缓存小 tips
precache 机制
- 安装成功不代表可以走
Service Worker
按照第一条,由于第一次打开时是无 service worker 的,所以即使安装成功了,激活了,请求也不会走 service worker。必须刷新页面才行。
解决方案:
修改vue.config.js
里面的pwa
pwa: {
workboxOptions: {
clientsClaim: true,
},
}
如果没有效果,可以尝试下以下链接里面的方案:
https://stackoverflow.com/questions/54145735/vue-pwa-not-getting-new-content-after-refresh
- 更新的时候,更新成功后要下一次打开网页才会生效
暂时我只知道一个比较暴力的办法,那就是,skipwaiting
同样是修改vue.config.js
pwa: {
workboxOptions: {
skipWaiting: true,
},
}
不过网上不是很推荐这种写法。我还没理解为什么,有知道的朋友可以给我留言。
参考文章
[https://www.zklighting.ltd/?p=1214](https://www.zklighting.ltd/?p=1214)