通过判断打包后的html文件中的js入口是否发生变化,进而实现前端的代码更新
为了使打包后的文件带有hash值,需要对vite打包进行配置
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'export default defineConfig({base: './',plugins: [vue(), AutoImport({resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver()],}),],envDir: './', // .env所在目录build: {target: 'es2015',outDir: 'dist',rollupOptions: {output: {manualChunks(id) { if (id.includes('node_modules')) {return id.toString().split('node_modules/')[1].split('/')[0].toString();}},// 文件附带上hashentryFileNames: '[name]-[hash].js',chunkFileNames: '[name]-[hash].js',assetFileNames: '[name]-[hash].[ext]'}},minify: 'esbuild', },resolve: {alias: {"@": resolve(__dirname, 'src'),},extensions: ['.vue', '.js']},server: {proxy: {'/api': {target: 'http://localhost:8083',changeOrigin: true,rewrite: (path) => path.replace(/^\/api/, "")}}},
});
update.js的主要功能是定期检查网页中的脚本标签是否有更新,并在检测到新版本时提示用户刷新页面以确保平台正常使用。
// update.js
import { ElMessageBox } from 'element-plus';let timer = undefined;
const url = import.meta.env.VITE_UPDATE_URL;function cmpSets(set1, set2) {if (set1.size !== set2.size) return false;for (let item of set1) {if (!set2.has(item)) return false;}return true;
}
// 更新提示框
function updateNotice() {ElMessageBox({title: '更新提示!',message: "检测到新版本,请立即刷新以确保平台正常使用",confirmButtonText: '确定',type: 'warning',}).finally(() => {window.location.reload();});
}// 获取页面中的脚本标签src属性的哈希值集合,忽略查询参数
async function getSrcHash() {try {const html = await fetch(url).then((res) => res.text());const scriptSrcRegex = /<script\b[^>]*src="([^"]*)"/gi;const scriptSrcs = [...html.matchAll(scriptSrcRegex)].map(match => match[1]);// 开发环境中入口文件包含保存时的时间戳,判断时移除此参数以免开发时一直提示更新const cleanSrcs = scriptSrcs.map(src => src.split('?')[0]); const encodedSrcs = new Set(cleanSrcs.map(src => encodeURIComponent(src)));return encodedSrcs;} catch (error) {console.error('Failed to fetch script hashes:', error);return new Set();}
}// 比较当前脚本标签哈希值与新获取的哈希值
async function cmpHash() {try {const newHash = await getSrcHash();const storedHash = JSON.parse(localStorage.getItem('curHash')) || [];// 如果是新用户或首次访问,直接保存哈希值并退出函数if (storedHash.length === 0) {localStorage.setItem('curHash', JSON.stringify([...newHash]));return;}// 合并新旧哈希值集合let curHash = new Set(storedHash);if (!cmpSets(curHash, newHash)) {console.info("new:", newHash);console.info("old:", curHash);console.log("更新提示")clearInterval(timer);updateNotice();}// 保存最新的哈希值集合到localStoragelocalStorage.setItem('curHash', JSON.stringify([...newHash]));} catch (error) {console.error('Error comparing script hashes:', error);}
}// 设置定时器,定期检查脚本更新
timer = setInterval(cmpHash, 30 * 1000);// 页面加载时比较哈希值
async function init() {await cmpHash();
}
// 页面加载时执行初始化函数
init();
最后在main.js入口文件引入即可
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import router from './router/index.ts'
import "./update.js" //引入自动更新脚本
import { createPinia } from 'pinia'const pinia = createPinia()
const app = createApp(App)
app.use(router)
app.use(pinia)
app.use(ElementPlus)
app.mount('#app')
通过篡改localstorage中的curHash,可引导更新,效果如下
30s更新一次算频繁吗?