前端项目部署自动检测更新后通知用户刷新页面(前端实现,技术框架vue、js、webpack)——方案一:编译项目时动态生成一个记录版本号的文件

前言

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

技术框架

vue、js、webpack

解决方案

  • 编译项目时动态生成一个记录版本号的文件
  • 轮询(20s、自己设定时间)这个文件,判断版本号,有新版本则通知用户刷新页面
  • 通过监听visibilitychange事件,在页面隐藏时停止轮询,页面显示立马检测一次更新
  • 检测到更新后,停止轮询

(感兴趣的可去看方案二:根据打完包之后生成的script src 的hash值去判断,每次打包都会生成唯一的hash值,只要轮询去判断不一样了,那一定是重新部署了。) 

效果

页面右下角提示更新:

代码实现 

 Step1:在 vue.config.js 实现动态创建版本号文件

if (process.env.VUE_APP_ENV !== "production") {
// 这里我设置的是只在非生产环境自动检测更新(生成version);
// 想要所有环境都自动检测更新,只要写if(process.env.VUE_APP_ENV !== "production")内的内容就好
  const { writeFile, existsSync } = require('fs')
  // 动态生成版本号
  const createVersion = () => {
    //检测目录是否存在
    if (existsSync('./public')) {
      writeFile(`./public/version.json`, `{"version":"${Date.now()}"}`, (err) => {
        if (err) {
          console.log('写入version.json失败')
          console.log(err)
        } else {
          console.log('写入version.json成功')
        }
      })
    } else {
      setTimeout(createVersion, 1000)
    }
  }
  setTimeout(createVersion, 10000)
}

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

/*
 * @Description: 自动更新
 */

let currentVersion // 当前版本
let version // 新版本
// const timeData = 60 * 1000 // 检查间隔时间
const timeData = 20 * 1000 // 检查间隔时间
let hidden = false // 页面是否隐藏
let setTimeoutId
let needTip = true // 默认开启提示

// 获取版本号
const getVersion = async () => {
  return fetch('/version.json?timestep=' + Date.now()).then((res) => res.json())
}

// 检查更新
const checkUpdate = async () => {
  console.log('***************checkUpdate**************')
  const currentVersion = sessionStorage.getItem("version")
  version = (await getVersion()).version
  // 本地没有 version,表示刚进入系统,直接塞值
  if (!currentVersion) return sessionStorage.setItem("version", version)

  console.log("🚀 ~ file: auto-update.js:19 ~ version:", version)
  console.log("🚀 ~ file: auto-update.js:21 ~ currentVersion:", currentVersion)
  console.log("🚀 ~ file: auto-update.js:23 ~ Number(version) !== Number(currentVersion):", Number(version) !== Number(currentVersion))
  let needRefresh = false
  if (Number(version) !== Number(currentVersion)) {
    console.log('%c 发现新版本~~~~~~', 'color: red')
    needRefresh = true
  }
  return needRefresh
}

// 自动更新
const autoUpdate = async () => {
  setTimeoutId = setTimeout(async () => {
    // 页面隐藏了就不检查更新
    if (!hidden) {
      const willUpdate = await checkUpdate()
      console.log("🚀 ~ file: auto-update.js:71 ~ setTimeoutId=setTimeout ~ willUpdate, version:", willUpdate, version)

      if (willUpdate && needTip) {
        // 延时更新,防止部署未完成用户就刷新空白
        setTimeout(()=>{

          // ----弹框确认---先简单点弹框确认,可以用注释内的,跳过右下角通知的内容(Step3、4)
          // 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 () => {
  // currentVersion = (await getVersion()).version
  autoUpdate()
  // 监听页面是否隐藏
  document.addEventListener('visibilitychange', () => {
    hidden = document.hidden
    console.log("🚀 ~ file: auto-update.js:64 ~ document.addEventListener ~ hidden, needTip:", hidden, needTip)
    // 页面隐藏了就不检查更新。或者已经有一个提示框了,防止重复提示。
    if (!hidden && needTip) {
      autoUpdate()
    } else {
      stop()
    }
  })
}

export default { start }

Step3:编写模板 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: '',
    },
    version: {
      type: String,
      default: '',
    },
  },
  data() {
    return {};
  },
  created() {},
  methods: {
    // 点击确定更新
    onSubmit() {
      sessionStorage.setItem('version', this.version) // 存入版本version
      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>

Step4: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>

 Step5:在 main.js 内使用

// 引入自动更新提醒
import autoUpdate from './auto-update'
// 非生产环境使用
process.env.VUE_APP_ENV !== 'production' && autoUpdate.start()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/192655.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MacOS14 Sonoma 安装 Flutter 开发环境

本文针对 小白用户也包括自己&#xff0c;以前都是将这些写入我的有道云笔记。为了让给多人看见或者说自己更好的浏览&#xff0c;先将其记录如下。 朋友介绍一个项目说要开发一款App&#xff0c;最近也是闲着就答应下来。主要功能是通过蓝牙BLE控制设备的一个 Iot边缘设备&…

2020年06月 Scratch(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共15题,每题2分,共30分) 第1题 执行下图程序后,“花名”列表的第3项是? A:莲花 B:丁香 C:合欢 D:月季 答案:C 列表基本知识,选C。 第2题 执行如下图所示程序后,其结果为? A: B:

P2704 [NOI2001] 炮兵阵地 题解

P2704 题目题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示 解题思路分析Code更多方法 题目 原题链接 题目描述 司令部的将军们打算在 N M N\times M NM 的网格地图上部署他们的炮兵部队。 一个 N M N\times M NM 的地图由 N N N 行 M M M 列组成&#x…

解决视口动画插件jquery.aniview.js使用animate.css时无效的问题(最新版本网页视口动画插件的使用及没作用、没反应)

当网站页面元素进入视口时自动应用过渡效果。CSS过渡效果可以为网页添加动画效果&#xff0c;并提供了一种平滑的转换方式&#xff0c;使元素的变化更加流畅和生动。而通过jQuery插件来获取页面滚动位置决定合适调用动画效果。 一、官网 animate.css官网 一款强大的预设css3动…

Unity - Graphic解析

Gpahic 的作用 Graphic 是 Unity最基础的图形基类。主要负责UGUI的显示部分。 由上图可以看你出我们经常使用的Image&#xff0c;Text&#xff0c;都是继承自Graphic。 Graphic的渲染流程 在Graphic的源码中有以下属性 [NonSerialized] private CanvasRenderer m_CanvasRend…

深度学习中的注意力机制:原理、应用与实践

深度学习中的注意力机制&#xff1a;原理、应用与实践 摘要&#xff1a; 本文将深入探讨深度学习中的注意力机制&#xff0c;包括其原理、应用领域和实践方法。我们将通过详细的解析和代码示例&#xff0c;帮助读者更好地理解和应用注意力机制&#xff0c;从而提升深度学习模…

图解算法数据结构-LeetBook-树03_层序遍历奇数偶数行方向不同

一棵圣诞树记作根节点为 root 的二叉树&#xff0c;节点值为该位置装饰彩灯的颜色编号。请按照如下规则记录彩灯装饰结果&#xff1a; 第一层按照从左到右的顺序记录 除第一层外每一层的记录顺序均与上一层相反。即第一层为从左到右&#xff0c;第二层为从右到左。 示例 1&…

【论文解读】Edit-DiffNeRF:使用2D-扩散模型编辑3D-NeRF

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 论文链接&#xff1a;https://arxiv.org/abs/2306.09551 摘要 最近的研究表明&#xff0c;将预训练的扩散模型与神经辐射场&#xff08;NeRF&#xff09;相结合&#xff0c;是一种很有前途的文本到 3D 的生成…

Java(119):ExcelUtil工具类(org.apache.poi读取和写入Excel)

ExcelUtil工具类(XSSFWorkbook读取和写入Excel)&#xff0c;入参和出参都是&#xff1a;List<Map<String,Object>> 一、读取Excel testdata.xlsx 1、new XSSFWorkbook对象 File file new File(filePath); FileInputStream fis new FileInputStream(…

如何获取抖音订单API数据接口?

在开放平台中&#xff0c;每个API接口都有相应的文档说明和授权机制&#xff0c;以确保数据的安全性和可靠性。开发者可以根据自己的需求选择相应的API接口&#xff0c;并根据文档说明进行调用和使用。 开放平台API接口是一套REST方式的开放应用程序编程接口&#xff0c;它…

【Qt】判断QList链表内是否有重复数据

QList<int> listInt;listInt.push_back(1);listInt.push_back(1);listInt.push_back(2);listInt.push_back(3);qDebug().noquote() << listInt.toSet().toList();

【Python】使用globals()函数成功解决tkinter多个新窗口问题

我在近期的一个项目&#xff08;tkinter复刻记事本&#xff09;上遇到一个很有意思的问题&#xff1a;如何在创建多个新窗口后&#xff0c;每个窗口还能独立运行&#xff1f;当时我尝试几种方法&#xff0c;奈何实力不足&#xff0c;于是便下定结论非使用线程不可&#xff0c;至…

YOLOv7独家原创改进: AKConv(可改变核卷积),即插即用的卷积,效果秒杀DSConv | 2023年11月最新发表

💡💡💡本文全网首发独家改进:可改变核卷积(AKConv),赋予卷积核任意数量的参数和任意采样形状,为网络开销和性能之间的权衡提供更丰富的选择,解决具有固定样本形状和正方形的卷积核不能很好地适应不断变化的目标的问题点,效果秒殺DSConv 1)AKConv替代标准卷积进行…

【全新升级】:Word、Excel和PPT批量转PDF - PyQt设计

文章目录 ✨前言✨脚本使用教程&#x1f4da;资源领取&#xff08;含源代码&#xff09; ✨前言 最近花了十几天的时间学习了PyQt的使用&#xff0c;发现PyQt具有丰富的特性和功能&#xff0c;可以创建出漂亮、交互性强的GUI应用程序&#xff0c;而且还可通过CSS样式表来设计界…

亥姆霍兹线圈磁场特点

亥姆霍兹线圈是一种由两个平行的同轴线圈组成的电磁装置&#xff0c;它的电流方向相反&#xff0c;且它们的半径相等&#xff0c;距离也相等。亥姆霍兹线圈的磁场特点主要有以下几个方面&#xff1a; 磁场均匀性高&#xff1a;亥姆霍兹线圈的磁场均匀性非常高&#xff0c;因为…

ESXi 添加新网络 配置ubuntu虚拟机双网卡

基本概念 在ESXi的虚拟机之间确保正常通信的基础是网络服务&#xff0c;通常在物理网络中需要使用不同的物理设备进行连接才能组建出高效的网络服务&#xff0c;而在虚拟网络中&#xff0c;需要不同的虚拟设备为其提供服务。 ESXi的网络类型&#xff1a; 1、物理网络&#xf…

lipid signaling

--Introduction to Lipid Signaling (csbsju.edu)

BGP路由的选路综合实验

题目要求 1.使用PreVal策略&#xff0c;确保R1通过R3到达192.168.10.0/24 2.使用AS_Path策略&#xff0c;确保R1通过R3到达192.168.11.0/24 3.配置MED策略&#xff0c;确保R1通过R3到达192.168.12.0/24 4.使用Local Preference策略&#xff0c;确保R4通过R2到达192.168.1.0/24…

office tool plus工具破解word、visio等软件步骤

第一步&#xff1a;下载工具 破解需要用到office tool plus软件 office tool plus软件下载地址&#xff1a;Office Tool Plus 官方网站 - 一键部署 Office 选择其中一个下载到本地&#xff08;本人选择的是第一个的云图小镇下载方式&#xff09; 第二步&#xff1a;启动工具 …

还不懂缓存穿透?Redis缓存穿透深度剖析

&#x1f388;个人公众号:&#x1f388; :✨✨✨ 可为编程✨ &#x1f35f;&#x1f35f; &#x1f511;个人信条:&#x1f511; 知足知不足 有为有不为 为与不为皆为可为&#x1f335; &#x1f349;本篇简介:&#x1f349;本篇记录Redis缓存穿透深度剖析命令操作&#xff0c;…