前端实现版本更新自动检测✅
🤖 作者简介:水煮白菜王,一位前端劝退师 👻
👀 文章专栏: 前端专栏 ,记录一下平时在博客写作中,总结出的一些开发技巧和知识归纳总结✍。
感谢支持💕💕💕
目录
- 一、背景
- 二、实现原理
- 2.1 逻辑
- 2.2 一些好处
- 三 、具体实现
- 3.1 封装
- 3.2 关键解析
- 哈希获取:
- 对比逻辑:
- 四、全部代码🚀🚀🚀
- 4.1 vue3
- 4.2 vue2
- 五、一些问题事项
- 5.1 可能出现的问题
- 5.2 浏览器兼容
一、背景
在现代Web应用中,部署前端版本更新后及时提醒用户进行页面刷新是必要的。由于单页面应用(SPA)的路由特性和浏览器缓存机制,用户浏览器可能不会自动加载最新的代码资源。这会导致用户未能体验到最新的功能变化,甚至遇Bug或不一致的行为。通过实现一种自动检测机制来提醒用户有新的版本,并引导其刷新页面,可以有效地解决这个问题,保证所有用户都能及时使用最新版应用的功能。
二、实现原理
2.1 逻辑
通过对比构建打包出文件的哈希值变化实现版本检测:
- 定时轮询:每分钟检查静态资源变化
- 哈希对比:通过解析HTML中script标签指纹判断更新
- 强制刷新:检测到更新后提示用户刷新页面
// 核心对比逻辑 const isChanged = (oldSet, newSet) => { return oldSet.size !== newSet.size || ![...oldSet].every(hash => newSet.has(hash)) }
2.2 一些好处
- 通用性强:适用于任意前端框架
- 无侵入式检测:不依赖构建工具配置
- 用户可控:提示框让用户选择刷新时机
- 精准检测:通过对比script标签内容哈希值
- 低资源消耗:每分钟检测一次,单次请求性能消耗低
三 、具体实现
3.1 封装
// useVersionHash.js 核心实现 export default function useVersionHash() { // 状态管理 const timerUpdate = ref(null) let scriptHashes = new Set() // 生命周期 onMounted(() => startTimer()) onBeforeUnmount(() => stopTimer()) // 业务方法 const fetchScriptHashes = async () => { /*...*/ } const compareScriptHashes = async () => { /*...*/ } return { compareScriptHashes } }
3.2 关键解析
哈希获取:
const fetchScriptHashes = async () => { const html = await fetch('/').then(res => res.text()) const scriptRegex = /]*)?>(.*?)/gi return new Set(html?.match(scriptRegex) || []) }
对比逻辑:
if (scriptHashes.size === 0) { // 初始化基准值 scriptHashes = newScriptHashes } else if ( scriptHashes.size !== newScriptHashes.size || ![...scriptHashes].every(hash => newScriptHashes.has(hash)) ) { // 触发更新流程 stopTimer() showUpdateDialog() }
四、全部代码🚀🚀🚀
4.1 vue3
1、use-version-update.js具体逻辑
// @/utils/use-version-update.js import { ref, onMounted, onBeforeUnmount } from 'vue' import { ElMessageBox } from 'element-plus' let scriptHashes = new Set() const timerUpdate = ref(null) export default function useVersionHash() { const isProduction = import.meta.env.MODE === 'production' const fetchScriptHashes = async () => { try { const html = await fetch('/').then((res) => res.text()) const scriptRegex = /]*)?>(.*?)/gi return new Set(html?.match(scriptRegex) || []) } catch (error) { console.error('获取脚本哈希失败:', error) return new Set() } } const compareScriptHashes = async () => { try { const newScriptHashes = await fetchScriptHashes() if (scriptHashes.size === 0) { scriptHashes = newScriptHashes } else if ( scriptHashes.size !== newScriptHashes.size || ![...scriptHashes].every(hash => newScriptHashes.has(hash)) ) { stopTimer() updateNotice() } } catch (error) { console.error('版本检查失败:', error) } } const updateNotice = () => { ElMessageBox.confirm( '检测到新版本,建议立即更新以确保平台正常使用', '更新提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' } ).then(() => window.location.reload()) } const startTimer = () => { if (!isProduction) return timerUpdate.value = setInterval(compareScriptHashes, 60000) } const stopTimer = () => { timerUpdate.value && clearInterval(timerUpdate.value) } onMounted(startTimer) onBeforeUnmount(stopTimer) return { compareScriptHashes, updateNotice } }
2、引入use-version-update.js
// App.vue import versionUpdatefrom '@/utils/use-version-update.js' export default { setup() { const { updateNotice } = versionUpdate() return { updateNotice } } }
3、Vite 相关配置
// vite.config.js import { defineConfig } from 'vite' export default defineConfig({ build: { rollupOptions: { output: { // 主入口文件命名规则 entryFileNames: 'js/[name]-[hash:8].js', // 代码分割块命名规则 chunkFileNames: 'js/[name]-[hash:8].js', // 静态资源文件命名规则 assetFileNames: ({ name }) => { const ext = name?.split('.').pop() return `assets/${ext}/[name]-[hash:8].[ext]` } } }, // 启用文件哈希的manifest生成 manifest: true } })
也可以将use-version-update写成以JS、TS模块化封装,在入口文件中main.ts引入
// use-version-update.ts export const versionUpdate = () => { ... 具体处理逻辑 } // main.ts import { versionUpdate} from "@/utils/use-version-update" if (import.meta.env.MODE == 'production') { versionUpdate() }
4.2 vue2
1、use-version-update.js具体逻辑
/* * @Author: baicaiKing * @Date: 2025-01-02 13:50:33 * @LastEditors: Do not edit * @LastEditTime: 2025-01-03 09:40:36 * @FilePath: \code\src\utils\use-version-update.js */ // 存储当前脚本标签的哈希值集合 let scriptHashes = new Set(); let timerUpdate = undefined; export default { data() { return { }; }, created() { }, mounted() { // 每60秒检查一次是否有新的脚本标签更新 if (process.env.NODE_ENV === 'production') { // 只针对生产环境 timerUpdate= setInterval(() => { this.compareScriptHashes() }, 60000); } }, beforeDestroy() { clearInterval(timerUpdate); timerUpdate = null; }, methods: { /** * 从首页获取脚本标签的哈希值集合 * @returns {Promise} 返回包含脚本标签的哈希值的集合 */ async fetchScriptHashes() { // 获取首页HTML内容 const html = await fetch('/').then((res) => res.text()); // 正则表达式匹配所有标签 const scriptRegex = /]*)?>(.*?)/gi; // 获取匹配到的所有标签内容 // const scripts = html.match(scriptRegex) ?? []; const scripts = html ? html.match(scriptRegex) || [] : []; // 将脚本标签内容存入集合并返回 return new Set(scripts); }, /** * 比较当前脚本标签的哈希值集合与新获取的集合,检测是否有更新 */ async compareScriptHashes() { // 获取新的脚本标签哈希值集合 const newScriptHashes = await this.fetchScriptHashes(); if (scriptHashes.size === 0) { // 初次运行时,存储当前脚本标签哈希值 scriptHashes = newScriptHashes; } else if ( scriptHashes.size !== newScriptHashes.size || ![...scriptHashes].every((hash) => newScriptHashes.has(hash)) ) { // 如果脚本标签数量或内容发生变化,则认为有更新 console.info('已检测到更新文件', { oldScript: [...scriptHashes], newScript: [...newScriptHashes], }); // 清除定时器 clearInterval(timerUpdate); // 提示用户更新 this.updateNotice(); } else { // 没有更新 console.info('未检测到更新时机', { oldScript: [...scriptHashes], }); } }, updateNotice() { this.$confirm('检测到新版本,建议立即更新以确保平台正常使用', '更新提示', { confirmButtonText: '确定', cancelButtonText: '取消(自行刷新)', type: 'warning' }).then(() => { window.location.reload(); }).catch(() => { console.eror('用户取消刷新!'); }); } }, };
2、引入use-version-update.js
// App.vue import versionUpdate from "@/util/use-version-update.js"; export default { name: "app", mixins: [versionUpdate], data() { return {}; }, };
3、Webpack 相关配置
// vue.config module.exports = { configureWebpack: { output: { filename: 'js/[name].[hash].js', // filename: 'js/[name].[contenthash].js', }, }, devServer: { }, };
五、一些问题事项
5.1 可能出现的问题
问题现象 可能原因 解决方案 检测不准确 正则匹配失效 更新正则表达式 生产环境未生效 环境变量配置错误 检查构建配置 跨域请求失败 部署路径不匹配 调整fetch请求路径 内存泄漏 定时器未正确清除 使用WeakRef优化 5.2 浏览器兼容
可结合Service Worker实现无缝更新
// 支持Service Worker的渐进增强方案 if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js') .then(reg => { reg.addEventListener('updatefound', () => { showUpdateNotification() }) }) }
同时要确保服务器配置正确缓存策略,通常Nginx缓存策略默认不用打理
如果你觉得这篇文章对你有帮助,请点赞 👍、收藏 👏 并关注我!👀
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。