纯前端实现更新检测

通过判断打包后的html文件中的js入口是否发生变化,进而实现前端的代码更新

为了使打包后的文件带有hash值,需要对vite打包进行配置

javascript 复制代码
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();
          }
        },
        // 文件附带上hash
        entryFileNames: '[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的主要功能是定期检查网页中的脚本标签是否有更新,并在检测到新版本时提示用户刷新页面以确保平台正常使用。

javascript 复制代码
// 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();
        }

        // 保存最新的哈希值集合到localStorage
        localStorage.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入口文件引入即可

javascript 复制代码
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更新一次算频繁吗?

相关推荐
Wang's Blog36 分钟前
Java 项目实战: 交友项目:登录链路回顾与今日任务地图
java·开发语言·状态模式·交友
砚底藏山河1 小时前
python量化:行情数据回溯
java·开发语言·python·金融·maven
今年下半年1 小时前
AI智能体项目实战记录(02):初始化 Python 项目
开发语言·人工智能·python·fastapi
Williamtym1 小时前
PickGo Lab实践:用浏览器、Python和MCP串起机器人搬运仿真
开发语言·python·机器人
DongQiShanRen2 小时前
六根硬件映射(四):归属澄清、两处不一致与卷十一总表
开发语言·人工智能·算法·rust·哈希算法·集成学习
CS创新实验室2 小时前
Python 对象“死“了之后去了哪?——引用计数与分代垃圾回收
开发语言·jvm·python
原则猫2 小时前
UDP 到底怎么实现可靠的
前端
Java后端的Ai之路2 小时前
14、ReactJS - 路由(Router)
前端·react.js·状态模式
持敬chijing2 小时前
白盒测试与代码审计-js登录逆向实战
javascript·网络·安全·web安全·网络安全·网络攻击模型
quantdash_cc2 小时前
Pandas 处理分钟级股票行情:从原始数据清洗到 K 线聚合
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash