前端项目部署自动检测更新后通知用户刷新页面(前端实现,技术框架vue、js、webpack)——方案二:轮询去判断服务端的index.html是否跟当前的index.html的脚本hash值一样

金乌Y 2024-06-27 16:03:03 阅读 69

前言

当我们重新部署前端项目的时候,如果用户一直停留在页面上并未刷新使用,会存在功能使用差异性的问题,因此,当前端部署项目后,需要提醒用户有去重新加载页面。

技术框架

vue、js、webpack

解决方案

根据打完包之后生成的script src 的hash值去判断,每次打包都会生成唯一的hash值,只要轮询去判断不一样了,那一定是重新部署了

轮询(20s、自己设定时间)服务器的index.html 文件,将新的script数组与旧script数组比较数组内容是否一致(可以将新旧数组拼接后去重,若去重后的数组长度,与旧数组长度不一样,则说明重新部署了),若新旧数组不一致则通知用户刷新页面通过监听visibilitychange事件,在页面隐藏时停止轮询,页面显示立马检测一次更新检测到更新后,停止轮询

(感兴趣的还可去看方案一:编译项目时动态生成一个记录版本号的文件,轮询请求该文件。前端项目部署自动检测更新后通知用户刷新页面(前端实现,技术框架vue、js、webpack)——方案一:编译项目时动态生成一个记录版本号的文件_新版本发布后,怎么用技术手段通知用户刷新页面?-CSDN博客)

效果

页面右下角提示更新:

代码实现 

 Step1:在src目录下封装 auto-update.js

/*

* @Description: 自动更新

*/

// const timeData = 60 * 1000 // 检查间隔时间

const timeData = 20 * 1000 // 检查间隔时间

let hidden = false // 页面是否隐藏

let setTimeoutId

let needTip = true // 默认开启提示

let oldScript = []

let newScript = []

const getHtml = async () => {

const html = await fetch('/').then(res => res.text()) //读取index html

return html

}

// const scriptReg = /<script.*src=["'](?<src>[^"']+)/gm

const parserScript = (html) => {

const reg = new RegExp(/<script(?:\s+[^>]*)?>(.*?)<\/script\s*>/ig) //script正则

return html.match(reg) //匹配script标签

}

const init = async () =>{

const html = await getHtml()

// console.log("🚀 ~ file: auto-update.js:31 ~ init ~ html:", html)

oldScript = parserScript(html)

console.log("🚀 ~ file: auto-update.js:30 ~ init ~ oldScript:", oldScript)

}

const compareScript = async (oldArr, newArr) => {

console.log('***************compareScript**************')

console.log("🚀 ~ file: auto-update.js:37 ~ compareScript ~ oldArr, newArr:", oldArr, newArr)

const base = oldArr.length

console.log("🚀 ~ file: auto-update.js:36 ~ compareScript ~ base:", base)

// 去重

const arr = Array.from(new Set(oldArr.concat(newArr)))

console.log("🚀 ~ file: auto-update.js:39 ~ compareScript ~ arr:", arr, arr.length)

let needRefresh = false

// 如果新旧length 一样无更新

// 否则通知更新

if (arr.length !== base) {

console.warn('更新了!!!!!!, arr.length !== base', arr.length !== base)

needRefresh = true

}

// for (let i = 0; i < oldArr.length; i++) {

// if (oldArr[i] !== arr[i]) {

// needRefresh = true

// console.warn('更新了!!!!!!, 值不等')

// break

// }

// }

return needRefresh

}

// 自动更新

const autoUpdate = async () => {

setTimeoutId = setTimeout(async () => {

const newHtml = await getHtml()

// console.log("🚀 ~ file: auto-update.js:89 ~ newHtml:", newHtml)

newScript = parserScript(newHtml)

console.log("🚀 ~ file: auto-update.js:79 ~ newScript:", newScript)

// 页面隐藏了就不检查更新

if (!hidden) {

const willRefresh = await compareScript(oldScript, newScript)

console.log("🚀 ~ file: auto-update.js:85 ~ setTimeoutId=setTimeout ~ willRefresh:", willRefresh)

if (willRefresh && needTip) {

// 延时更新,防止部署未完成用户就刷新空白

setTimeout(()=>{

// ----弹框确认---先简单点弹框确认,可以用注释内的,跳过右下角通知的内容(Step2、3)

// const result = confirm('发现新版本,点击确定更新')

// if (result) {

// sessionStorage.setItem('version', version)

// location.reload() // 刷新当前页

// }

// --------------

//*****右下角通知提示 */

window.dispatchEvent(

new CustomEvent("onmessageUpdate", {

detail: {

msg: "发现系统版本更新,请刷新页面~",

version: version

},

})

)

//******************* */

}, 10000)

needTip = false // 关闭更新提示,防止重复提醒

}

}

console.log("🚀 ~ file: auto-update.js:90 ~ autoUpdate ~ needTip: ", needTip)

if (needTip) {

console.warn('needTip autoUpdate');

autoUpdate()

}

}, timeData)

}

// 停止检测更新

const stop = () => {

if (setTimeoutId) {

clearTimeout(setTimeoutId)

setTimeoutId = ''

}

}

// 开始检查更新

const start = async () => {

init()

console.log('start0000000000')

autoUpdate()

console.log('start1111111111')

// 监听页面是否隐藏

document.addEventListener('visibilitychange', () => {

hidden = document.hidden

console.log("🚀 ~ file: auto-update.js:64 ~ document.addEventListener ~ hidden, needTip:", hidden, needTip)

// 页面隐藏了就不检查更新。或者已经有一个提示框了,防止重复提示。

if (!hidden && needTip) {

console.log('!!!checkupdate', '222222222');

autoUpdate()

} else {

stop()

}

})

}

export default { start }

Step2:编写模板 CnNotify.vue 文件

<template>

<div class="cn_notify">

<div class="content">

<i class="el-icon-message-solid"></i>

{ { msg }}

</div>

<div class="footer">

<el-row class="btnBox">

<el-button type="primary" @click="onSubmit">确认刷新</el-button>

<el-button @click="cancle">我知道了</el-button>

</el-row>

</div>

</div>

</template>

<script>

export default {

props: {

msg: {

type: String,

default: '',

},

},

data() {

return {};

},

created() {},

methods: {

// 点击确定更新

onSubmit() {

location.reload() // 刷新

},

// 关闭

cancle() {

this.$parent.close();

},

},

};

</script>

<style lang='scss' scoped>

.cn_notify {

.content {

padding: 20px 0;

}

.footer {

display: flex;

justify-content: center;

}

}

</style>

<style lang='scss'>

.versionNotifyStyle {

.el-notification__content {

width: 280px !important;

}

}

</style>

Step3:app.vue 使用组件CnNotify

<template>

<div id="app">

<router-view />

</div>

</template>

<script>

// 引入CnNotify组件

import CnNotify from "@/components/common/CnNotify/index.vue"

export default {

name: 'App',

components: {

CnNotify, // 注册组件

},

mounted() {

this.watchUpdate()

},

methods: {

watchUpdate() {

window.addEventListener("onmessageUpdate", (res) => {

console.log("🚀 ~ file: App.vue:20 ~ window.addEventListener ~ res:", res)

let msg = res.detail.msg,

version = res.detail.version

this.$notify({

title: "版本更新提示",

duration: 0,

position: "bottom-right",

dangerouslyUseHTMLString: true,

message: this.$createElement("CnNotify", {

// 使用自定义组件

ref: "CnNotify",

props: {

msg: msg,

version: version

},

}),

customClass:'versionNotifyStyle', //自定义类名

})

})

},

},

}

</script>

 Step4:在 main.js 内使用

// 引入自动更新提醒

import autoUpdate from './auto-update'

// 非生产环境使用

process.env.VUE_APP_ENV !== 'production' && autoUpdate.start()



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。