uni-app添加PWA支持

前提

确定你的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")
# img_deal = origin_img.resize((300,300),Image.ANTIALIAS) # 转化图片
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

// Detects if device is on iOS
export const isIos = () => {
  const userAgent = window.navigator.userAgent.toLowerCase();
  return /iphone|ipad|ipod/.test( userAgent );
}

// Detects if device is in standalone mode
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 机制

  1. 安装成功不代表可以走Service Worker
sw-install

按照第一条,由于第一次打开时是无 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

2. 更新的时候,更新成功后要下一次打开网页才会生效

暂时我只知道一个比较暴力的办法,那就是,skipwaiting

同样是修改vue.config.js

pwa: {
  workboxOptions: {
      skipWaiting: true,
  },
}

不过网上不是很推荐这种写法。我还没理解为什么,有知道的朋友可以给我留言。

参考文章

1 条评论

  1. jin

    请问你的包还在吗?我执行vue add vue-cli-plugin-unipwa后没有反应

发表评论

您的电子邮件地址不会被公开,必填项已用*标注。

相关推荐