在现代Web应用开发中,实现版本检测和自动更新提示是一个常见的需求。常见的实现方式也有很多,本文将主要介绍两种简单有效的纯前端版本检测方法。

一、静态资源变更检测:基于script标签的文件对比

核心思路

通常一些打包构建工具在每次打包时会根据文件信息自动生成文件指纹,每当文件的内容发生改变,文件指纹的值都会发生变化,我们可以运用这个特性来对比引入的文件资源。
通过抓取页面中 script 标签的 src 链接,对比当前已加载的资源列表,判断是否有文件新增、删除或路径变更,从而触发更新提示。

关键代码解析

1.获取最新页面的script链接

const getLatestScript = async () => {
  const scriptReg = /\<script.*src=["'](?<src>[^"']+)/gm; // 正则匹配src属性
  const res = await fetch(`/?t=${Date.now()}`); // 带时间戳避免缓存
  const html = await res.text();
  const result = [];
  let match;
  while ((match = scriptReg.exec(html))) {
    match.groups?.src && result.push(match.groups.src); // 提取所有有效链接
  }
  return result;
};

2.对比资源列表判断更新

let curScriptList = []
const checkUpdate = async () => {
  const latest = await getLatestScript();
  if (curScriptList.length === 0) { // 首次加载直接保存
    curScriptList = latest;
    return false;
  }
  // 长度不一致或内容不一致则视为更新
  return latest.length !== curScriptList.length || 
         latest.some((item, i) => item !== curScriptList[i]);
};

3.定时检测触发

setInterval(async () => {
  const hasUpdate = await checkUpdate();
  if (hasUpdate) {
    ElMessage.info('检测到更新,刷新获取新版本');
  }
}, 60*1000); // 每60秒检测一次

二、版本号对比检测:基于json文件的版本管理

核心思路

在构建时手动或自动生成包含版本号的json文件(如时间戳或自定义版本号),前端定期请求该文件,对比本地存储的版本号,判断是否需要更新。(第一步和第二步的目的是为了每次打包时自动生成版本号,如果是手动生成请忽略)

关键代码解析

1.构建时生成版本文件(Vite 插件)

import fs from 'fs';
import path from 'path';

export default function viteVersionPlugin(option = { fileName: 'version.json' }) {
  return {
    name: 'version-plugin',
    generateBundle({ dir = './dist' }) {
      const versionPath = path.join(dir, option.fileName);
      // 写入时间戳作为版本号(可替换为自定义版本号)
      fs.writeFileSync(versionPath, JSON.stringify({ version: Date.now() }));
    }
  };
}

2.在 Vite 配置文件中使用插件

重点关注第7、24行代码

import { fileURLToPath, URL } from 'node:url'
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import ViteVersionPlugin from './src/utils/viteVersionPlugin'   // 引入插件!!!
// https://vite.dev/config/
export default defineConfig(({ mode }) => {
    // 根据当前工作目录中的 `mode` 加载 .env 文件
    // 设置第三个参数为 '' 来加载所有环境变量,而不管是否有
    // `VITE_` 前缀。
    const env = loadEnv(mode, process.cwd(), '')
    return {
        plugins: [
            vue(),
            AutoImport({
                resolvers: [ElementPlusResolver()],
                imports: ['vue']
            }),
            Components({
                resolvers: [ElementPlusResolver()]
            }),
            ViteVersionPlugin()   // 使用插件!!!
        ],
        resolve: {
            alias: {
                '@': fileURLToPath(new URL('./src', import.meta.url))
            }
        },
        css: {
            preprocessorOptions: {
                scss: {
                    additionalData: `@use "@/assets/styles/variables.scss" as *;`,
                    api: 'modern-compiler'
                }
            }
        },
        server: {
            host: '0.0.0.0' // 实现内网访问
        },
        esbuild: {
            drop: mode === 'production' ? ['console', 'debugger'] : [] // 打包去除console 和 debugger
        },
        build: {
            outDir: './dist' // 指定输出路径
        }
    }
})

3.获取并对比版本号

let version = ''
const getLatestVersion = async () => {
  try {
    const res = await fetch(`/version.json?t=${Date.now()}`);
    return (await res.json()).version; // 读取远程版本号
  } catch (error) {
    console.error('获取版本失败', error);
    return '';
  }
};

const checkUpdateVersion = async () => {
  const latest = await getLatestVersion();
  if (!version || !latest) { // 首次加载保存版本号
    version = latest;
    return false;
  }
  return version !== latest; // 版本号不一致则触发更新
};

4.定时检测触发

同方法一

总结

两种方案各有优劣,实际开发中可结合使用:
静态资源检测用于监控前端代码文件的细微变更(如热更新场景)。仅做检测,无法精确到具体的版本。
版本号检测用于管理整体版本发布,配合 CI/CD 流程自动更新版本号。可在json文件中配置更多内容以获取精确的版本信息。
通过合理的版本检测机制,可确保用户始终使用最新版本的应用,同时避免频繁无意义的更新提示,平衡性能与体验。