Vue2 PWA可以使网页应用可安装,并在页面提示新版本刷新。

上面就是两张效果图都是网页应用


添加cli-plugin-pwa

给现有的vue项目添加,使用下面命令:

vue add pwa 
npm install register-service-worker
// 如果报错的话 就是用下面的
npm install -D @vue/cli-plugin-pwa

这个依赖的版本最好跟 @vue/cli-plugin-babel … 一样就好

执行完之后会自动在package.json添加”@vue/cli-plugin-pwa”,“register-service-worker” 依赖。

并且在项目src目录下生成registerServiceWorker.jsservice-worker.js文件:

registerServiceWorker.js


/* eslint-disable no-console */

import { register } from 'register-service-worker'

if (process.env.NODE_ENV === 'production') {
  register(`${process.env.BASE_URL}service-worker.js`, {
    ready () {
      console.log(
        'App is being served from cache by a service worker.\n' +
        'For more details, visit https://goo.gl/AFskqB'
      )
    },
    registered () {
      console.log('Service worker has been registered.')
    },
    cached () {
      console.log('Content has been cached for offline use.')
    },
    updatefound () {
      console.log('New content is downloading.')
    },
    updated () {
      console.log('New content is available; please refresh.')
    },
    offline () {
      console.log('No internet connection found. App is running in offline mode.')
    },
    error (error) {
      console.error('Error during service worker registration:', error)
    }
  })
}

service-worker.js 略有改动 以你自己的为准


// install new service worker when ok, then reload page.
self.addEventListener("message", msg => {
  if (msg.data && msg.data.type === 'SKIP_WAITING'){
      self.skipWaiting()
  }
})

而且还会在public/img/icons下生成适用于个平台安装的默认图标,可以自己生成替换即可。

并且在public下新建manifest.json

manifest.json

{
  "name": "My App",
  "short_name": "My App",
  "icons": [
    {
      "src": "./img/icons/apple-touch-icon.png",
      "sizes": "120x120",
      "type": "image/png"
    },
    {
      "src": "./img/icons/android-chrome-256x256.png",
      "sizes": "256x256",
      "type": "image/png"
    }
  ],
  "start_url": "./index.html",
  "display": "fullscreen",
  "background_color": "#fedfe1",
  "theme_color": "#fedfe1"
}

然后需要手动在main.js引入 registerServiceWorker.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './registerServiceWorker'

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

最后到vue.config.js中添加相关配置:

module.exports = {
	pwa: {
	    name: "My App",
            themeColor: "#ffffff",
            msTileColor: "#ffffff",
    	    assetsVersion: "3.0",
    	    iconPaths: {
      	    	    favicon32: 'img/icons/favicon-32x32.png',
      	    	    favicon16: 'img/icons/favicon-16x16.png',
      	    	    appleTouchIcon: 'img/icons/android-chrome-256x256.png',
      	    	    maskIcon: 'img/icons/apple-touch-icon.png', // 120x120
      	    	    msTileImage: 'img/icons/msapplication-icon-144x144.png',
    	    },
    	    workboxPluginMode: "InjectManifest",
    	    workboxOptions: {
      	    	    swSrc: "src/service-worker.js",
    	    },
}

这样重新npm run build:prod后,刷新页面后你的网站地址栏已经出现可安装的图标。

接下来我们解决意外一个问题 就是每次重新部署后客户端缓存问题,通过service worker可以提示用户有新的可用版本🔽

vue项目重新build在服务端部署后,浏览器刷新页面弹出的提示,这时如果用户点击更新就会重载页面,清除之前的缓存获取最新内容。

这是怎样发生的呢?你可能会想到下面的方式:

  • 服务端编译重新部署维护一个版本号,客户端通过轮询检测和本地存储的是否相同,发现更新的版本就弹框提示(缺点 耗电。尤其是在移动端)
  • 通过在html中做版本标记…
  • websocket长连接像客户端推送版本更新(繁琐)
  • service worker

通过观察截图左下角的红框,可以看出这个网站采用方式是 注册了 service worker

这种方式只需前端处理,不需要服务端做任何工作。只要每次build后重新在服务端部署,有文件发生变动,就可以被service worker发现。

registerServiceWorker.js添加事件触发

/* eslint-disable no-console */

import { register } from 'register-service-worker'

if (process.env.NODE_ENV === 'production') {
  register(`${process.env.BASE_URL}service-worker.js`, {
    ready () {
      console.log(
        'App is being served from cache by a service worker.\n' +
        'For more details, visit https://goo.gl/AFskqB'
      )
    },
    registered () {
      console.log('Service worker has been registered.')
    },
    cached () {
      console.log('Content has been cached for offline use.')
    },
    updatefound () {
      console.log('New content is downloading.')
    },
    updated (registration) {
      console.log('New content is available; please refresh.')
      document.dispatchEvent(
        new CustomEvent('swUpdated', { detail: registration })
      )
    },
    offline () {
      console.log('No internet connection found. App is running in offline mode.')
    },
    error (error) {
      console.error('Error during service worker registration:', error)
    }
  })
}

updated() {} 中添加swUpdated自定义事件,然后在ReloadPrompt.vue组件中监听事件,有更新时弹出 提示用户更新。

这个组件需要在App.vue中引入,或者其他合适的地方。

<template>
  <div>
    <transition name="el-zoom-in-bottom">
      <el-card class="fix_right_bottom" v-if="visible">
        <div style="padding: 14px;">
          <span style="font-size: 14px;">
            发现新版本,点击"更新"获取。
          </span>
          <div class="button_group">
            <el-button size="mini" @click="handlePrompt(false)">关闭</el-button>
            <el-button type="primary" size="mini" @click="refreshApp">更新</el-button>
          </div>
        </div>
      </el-card>
    </transition>
  </div>
</template>

<script>
export default {
  created() {
    document.addEventListener('swUpdated', this.updateAvailable, { once: true })

    navigator.serviceWorker.addEventListener('controllerchange', () => {
      // We'll also need to add 'refreshing' to our data originally set to false.
      if (this.refreshing) return
      this.refreshing = true
      // Here the actual reload of the page occurs
      window.location.reload()
    })
  },
  data() {
    return {
      visible: false,
      registration: null,
      refreshing: false
    }
  },
  methods: {
    handlePrompt(val) {
      this.visible = val
    },
    updateAvailable(event) {
      this.registration = event.detail
      this.visible = true
    },
    refreshApp() {
      this.visible = false
      // Make sure we only send a 'skip waiting' message if the SW is waiting
      if (!this.registration || !this.registration.waiting) return
      // Send message to SW to skip the waiting and activate the new SW
      this.registration.waiting.postMessage({ type: 'SKIP_WAITING' })
    }
  }
}
</script>

<style scoped>
.fix_right_bottom {
  position: fixed;
  right: 20px;
  bottom: 20px;
}
.button_group {
  margin-top: 10px;
}
</style>

service-worker.js

// install new service worker when ok, then reload page.
self.addEventListener("message", msg => {
    if (msg.data && msg.data.type === 'SKIP_WAITING'){
        self.skipWaiting()
    }
})

重新来看一下这个流程,上一版的网页注册了service-worker.js,服务端重新build部署,有新内容出现,

客户端刷新 => 发现有新的service-worker可以安装 => 触发updated(){} => ReloadPrompt.vue => 触发一次 swUpdated => 弹出更新弹框用户点击更新 => 向service-worker.js传递SKIP_WAITING => 新的service-worker.js安装后 => 触发ReloadPrompt.vue中的controllerchange 实现页面重载

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇