Vite + Vue3项目版本更新检查与页面自动刷新方案

使用 Vite 对 Vue 项目进行打包,对 js 和 css 文件使用了 chunkhash 进行了文件缓存控制,但是项目的 index.html 文件在版本频繁迭代更新时,会存在被浏览器缓存的情况。

在发版后,如果用户不强制刷新页面,浏览器会使用旧的 index.html 文件,在跳转页面时会向服务器端请求了上个版本 chunkhash 的 js 和 css 文件,但此时的文件已经在版本更新时已替换删除了,最终表现为页面卡顿,控制台报错 404。

解决思路

在每次打包生产代码时,在 public 目录下生成一个 version.json 版本信息文件,页面跳转时请求服务器端的 version.json 中的版本号和浏览器本地缓存的版本号进行对比,从而监控版本迭代更新,实现页面自动更新,获取新的 index.html 文件(前提是服务器端对 index.htmlversion.json 不缓存)。

第一步:配置服务器,禁止关键文件缓存

要实现版本对比,需确保 index.html 和后续生成的版本文件 version.json 不被浏览器缓存 ------ 每次请求均从服务器获取最新内容。以 Nginx 为例,添加如下配置:

conf 复制代码
location ~ .*\.(htm|html|json)?$ {
    expires -1;
}

第二步:开发 Vite 插件,自动生成版本信息

通过自定义 Vite 插件,在打包时自动在 public 目录下生成 version.json 文件,记录当前版本标识(建议用时间戳,确保每次打包版本号唯一)。 TypeScript 版本插件(src/plugins/versionUpdatePlugin.ts)

TypeScript 复制代码
// versionUpdatePlugin.ts
import fs from "node:fs"
import path from "node:path"

import type { ResolvedConfig } from "vite"

function writeVersion(versionFile: string, content: string) {
  // 写入文件
  fs.writeFile(versionFile, content, (err) => {
    if (err) throw err
  })
}

export default (version: string | number) => {
  let config: ResolvedConfig
  return {
    name: "version-update",
    configResolved(resolvedConfig: ResolvedConfig) {
      // 存储最终解析的配置
      config = resolvedConfig
    },
    buildStart() {
      // 生成版本信息文件路径
      const file = config.publicDir + path.sep + "version.json"
      // 这里使用编译时间作为版本信息
      const content = JSON.stringify({ version })
      if (fs.existsSync(config.publicDir)) {
        writeVersion(file, content)
      } else {
        fs.mkdir(config.publicDir, (err) => {
          if (err) throw err
          writeVersion(file, content)
        })
      }
    },
  }
}

JavaScript 版本插件(src/plugins/versionUpdatePlugin.js)

JavaScript 复制代码
// versionUpdatePlugin.js
const fs = require("fs")
const path = require("path")

const writeVersion = (versionFile, content) => {
  // 写入文件
  fs.writeFile(versionFile, content, (err) => {
    if (err) throw err
  })
}

export default (options) => {
  let config

  return {
    name: "version-update",
    configResolved(resolvedConfig) {
      // 存储最终解析的配置
      config = resolvedConfig
    },
    buildStart() {
      // 生成版本信息文件路径
      const file = config.publicDir + path.sep + "version.json"
      // 这里使用编译时间作为版本信息
      const content = JSON.stringify({ version: options.version })
      if (fs.existsSync(config.publicDir)) {
        writeVersion(file, content)
      } else {
        fs.mkdir(config.publicDir, (err) => {
          if (err) throw err
          writeVersion(file, content)
        })
      }
    },
  }
}

第三步:配置 Vite,注入全局版本变量

vite.config.js/ts 中引入上述插件,同时定义全局版本变量 __APP_VERSION__(供前端对比使用),建议用当前时间戳作为版本号,确保每次打包版本唯一。

类型声明(TS 项目必备)

若使用 TypeScript,需在 vite-env.d.tsenv.d.ts 中添加全局变量类型声明,避免类型报错:

typescript 复制代码
// vite-env.d.ts
declare const __APP_VERSION__: string

typescript 复制代码
// vite.config.js or vite.config.ts
export default defineConfig((config) => {
  const now = new Date().getTime()
  return {
    // ...
    define: {
      // 定义全局变量
      __APP_VERSION__: now,
    },
    plugins: [
      // ...
      versionUpdatePlugin({
        version: now,
      }),
    ],
    // ...
  }
})

第四步:前端实现版本检测,触发自动刷新

利用 Vue Router 的全局前置守卫,在每次页面跳转前检查版本 ------ 对比浏览器端全局变量 __APP_VERSION__ 与服务器端 version.json 中的版本号,若不一致则提示用户并自动刷新页面(选择前置守卫是因为跳转失败不会触发后置守卫,可在报错前完成检测)。

javascript 复制代码
const router = useRouter()
// 这里在路由全局前置守卫中检查版本
router.beforeEach(async () => {
  await versionCheck()
})

// 版本监控
const versionCheck = async () => {
  if (import.meta.env.MODE === "development") return
  const response = await axios.get("version.json")
  if (__APP_VERSION__ !== response.data.version) {
    ElMessage({
      message: "发现新内容,自动更新中...",
      type: "success",
      showClose: true,
      duration: 1500,
      onClose: () => {
        window.location.reload()
      },
    })
  }
}

如果大家有任何建议或疑问,都可以在下方评论或私信我,真的非常感谢!

✅ 觉得某部分设计太繁琐,有更简洁 / 高级的封装方式

✅ 发现代码能用新 API 或语法优化

✅ 对内容有不理解的地方

✅ 能解答我文中没说透的疑问

✅ 发现交互 / 功能 bug,或有优化建议

也欢迎大家实用我刚刚用Vue开发的标签页项目:NBtab新标签页

最后感谢大家的耐心阅读,既然都看到这了,点个👍赞再走吧!

相关推荐
To_OC1 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic2 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy2 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志2 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble3 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯4 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜5 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司5 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
江华森5 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http
IT_陈寒6 小时前
Redis的KEYS命令把我搞崩溃了,改用SCAN才活过来
前端·人工智能·后端