#Vue3篇: Vue 项目发版后如何提示用户刷新?基于 package.json 版本号的前端更新检测方案
2026/7/11 4:30:13 网站建设 项目流程

核心思路

利用package.json中的version字段作为版本标识

  1. 构建时:将 version 注入前端代码,同时生成version.json到产物目录
  2. 运行时:轮询服务器上的version.json,与本地版本对比
  3. 不一致时:弹窗提示用户刷新,加载新资源
发版:package.json version 递增 → build → dist 含 version.json + 代码内嵌版本号 ↓ 运行:本地版本(构建写入) vs 远程 version.json(轮询) ↓ 更新:版本不一致 → 弹窗 → location.reload()

一、版本号来源

统一从package.json读取,发版前手动递增或使用npm version自动递增:

# 自动递增并打 git tag(可选)npmversion patch# 1.2.3 → 1.2.4npmversion minor# 1.2.3 → 1.3.0npmversion major# 1.2.3 → 2.0.0
// package.json{"name":"my-app","version":"1.2.3"}

二、Vue2 接入(vue.config.js)

1. 顶部引入

constwebpack=require("webpack");const{version}=require("./package.json");

推荐注入为process.env.VUE_APP_VERSION,与项目已有的VUE_APP_*环境变量风格一致。

2. configureWebpack:生产环境注入版本号

在现有configureWebpackproduction 分支中,于config.plugins.push处加入DefinePlugin,与 TerserPlugin、CompressionWebpackPlugin 等插件共存:

configureWebpack:config=>{if(process.env.NODE_ENV==="production"){config.mode="production";// 注入版本号config.plugins.push(newwebpack.DefinePlugin({"process.env.VUE_APP_VERSION":JSON.stringify(version)}));config.optimization={// ... 原有 TerserPlugin、splitChunks 等配置};config.plugins.push(newCompressionWebpackPlugin({// ... 原有压缩配置}));}else{config.mode="development";}}

3. chainWebpack:构建时生成 version.json

chainWebpack末尾追加,仅生产环境生效,与 DLL、less 全局变量、alias、svg-sprite-loader 等配置互不影响:

chainWebpack:config=>{// ... 原有 dll、less、alias、svg 等配置if(process.env.NODE_ENV==="production"){config.plugin("version-json").use(classVersionJsonPlugin{apply(compiler){compiler.hooks.emit.tapAsync("VersionJsonPlugin",(compilation,callback)=>{constcontent=JSON.stringify({version,buildTime:newDate().toISOString()// 可选,便于排查});compilation.assets["version.json"]={source:()=>content,size:()=>content.length};callback();});}});}}

构建后产物结构:

dist/ ├── index.html ├── version.json ← 运行时轮询此文件 ├── js/ │ ├── app.[hash].js │ └── vendor.[hash].js └── css/ └── app.[hash].css

4. 与现有 webpack 配置的兼容性

现有配置是否冲突说明
publicPath: './'⚠️ 需注意轮询路径必须用process.env.BASE_URL,不能用/version.json
CompressionWebpackPlugin✅ 无冲突仅压缩 js/html/css,不影响 version.json
DLL 插件✅ 无冲突版本检测独立于 DLL 预构建
drop_console: true✅ 无冲突检测模块避免 console.log 即可
splitChunks✅ 无冲突版本号在独立 version.json 中

三、Vue3 接入(vite.config.js)

// vite.config.jsimport{defineConfig}from"vite";importvuefrom"@vitejs/plugin-vue";import{readFileSync,writeFileSync}from"fs";import{resolve}from"path";constpkg=JSON.parse(readFileSync("./package.json","utf-8"));exportdefaultdefineConfig({base:"./",// 相对路径部署时与 Vue2 publicPath: './' 等效plugins:[vue(),{name:"generate-version-json",closeBundle(){writeFileSync(resolve(__dirname,"dist/version.json"),JSON.stringify({version:pkg.version,buildTime:newDate().toISOString()}));}}],define:{__APP_VERSION__:JSON.stringify(pkg.version)}});

也可用import pkg from "./package.json"(需tsconfig开启resolveJsonModule)。

TypeScript 声明(可选):

// env.d.tsdeclareconst__APP_VERSION__:string;

四、运行时版本检测

1. 检测策略

Vue2Vue3
本地版本process.env.VUE_APP_VERSION__APP_VERSION__
远程版本fetch(\${process.env.BASE_URL}version.json)`fetch(\${import.meta.env.BASE_URL}version.json)`
防缓存URL 加?t=${Date.now()},请求头cache: 'no-store'同左
触发时机页面加载 + 定时轮询(5~30 min)+visibilitychange切回同左
环境限制production启用import.meta.env.PROD启用

2. Vue2 检测模块

// src/utils/versionCheck.jsconstLOCAL_VERSION=process.env.VUE_APP_VERSION;constVERSION_URL=`${process.env.BASE_URL}version.json`;constCHECK_INTERVAL=5*60*1000;lettimer=null;lethasNotified=false;asyncfunctioncheckVersion(onUpdate){try{constres=awaitfetch(`${VERSION_URL}?t=${Date.now()}`,{cache:"no-store"});if(!res.ok)return;const{version}=awaitres.json();if(version&&version!==LOCAL_VERSION&&!hasNotified){hasNotified=true;onUpdate(version);}}catch(e){// 网络异常静默处理,下次轮询再试}}exportfunctionstartVersionCheck(onUpdate){if(process.env.NODE_ENV!=="production")return;checkVersion(onUpdate);timer=setInterval(()=>checkVersion(onUpdate),CHECK_INTERVAL);document.addEventListener("visibilitychange",()=>{if(document.visibilityState==="visible")checkVersion(onUpdate);});}exportfunctionstopVersionCheck(){if(timer)clearInterval(timer);}

3. Vue3 检测模块

// src/utils/versionCheck.jsconstLOCAL_VERSION=__APP_VERSION__;constVERSION_URL=`${import.meta.env.BASE_URL}version.json`;constCHECK_INTERVAL=5*60*1000;lettimer=null;lethasNotified=false;asyncfunctioncheckVersion(onUpdate){try{constres=awaitfetch(`${VERSION_URL}?t=${Date.now()}`,{cache:"no-store"});if(!res.ok)return;const{version}=awaitres.json();if(version&&version!==LOCAL_VERSION&&!hasNotified){hasNotified=true;onUpdate(version);// onUpdate(version) = 「发现新版本后的处理函数」;versionCheck.js 只负责检测并调用它,具体弹窗、刷新等逻辑在 main.js 传入的回调里写。}}catch(e){// 静默失败}}exportfunctionstartVersionCheck(onUpdate){if(!import.meta.env.PROD)return;checkVersion(onUpdate);timer=setInterval(()=>checkVersion(onUpdate),CHECK_INTERVAL);document.addEventListener("visibilitychange",()=>{if(document.visibilityState==="visible")checkVersion(onUpdate);});}exportfunctionstopVersionCheck(){if(timer)clearInterval(timer);}

4. 入口挂载

Vue2(Element UI 示例):

// main.jsimport{startVersionCheck}from"@/utils/versionCheck";import{MessageBox}from"element-ui";startVersionCheck(newVersion=>{MessageBox.confirm(`发现新版本${newVersion}(当前${process.env.VUE_APP_VERSION}),是否立即刷新?`,"版本更新",{confirmButtonText:"刷新",cancelButtonText:"稍后",type:"info"}).then(()=>location.reload());});

Vue3(Element Plus 示例):

// main.jsimport{startVersionCheck}from"@/utils/versionCheck";import{ElMessageBox}from"element-plus";startVersionCheck(newVersion=>{ElMessageBox.confirm(`发现新版本${newVersion}(当前${__APP_VERSION__}),是否立即刷新?`,"版本更新",{confirmButtonText:"刷新",cancelButtonText:"稍后",type:"info"}).then(()=>location.reload());});

五、publicPath / base 路径对照

这是最容易踩坑的地方:

部署方式Vue2 publicPathVue3 baseversion.json 请求路径
根路径部署///version.json
相对路径部署././${BASE_URL}version.json
子目录部署/app//app//app/version.json

process.env.BASE_URL(Vue2)和import.meta.env.BASE_URL(Vue3)会自动与publicPath/base保持一致,推荐始终通过 BASE_URL 拼接路径


六、发版流程

1. package.json 递增 version(或 npm version patch) 2. npm run build 3. 部署 dist 目录(确保 version.json 一并上传) 4. 老用户浏览器运行旧 JS(旧版本号) 5. 轮询到新 version.json → 弹窗提示 → 用户刷新 → 加载新资源

七、Nginx 缓存配置

index.htmlversion.json必须禁用强缓存,否则用户永远拿不到新版本信息;带 contenthash 的 JS/CSS 可长期缓存:

# version.json 不缓存 location ~* version\.json$ { add_header Cache-Control "no-cache, no-store, must-revalidate"; } # index.html 不缓存 location = /index.html { add_header Cache-Control "no-cache, no-store, must-revalidate"; } # JS/CSS 带 hash,长期缓存 location ~* \.(js|css)$ { add_header Cache-Control "public, max-age=31536000, immutable"; }

八、可选优化

1. 多 Tab 去重弹窗

constnotifiedKey="notified_version";functionshouldNotify(newVersion){if(localStorage.getItem(notifiedKey)===newVersion)returnfalse;localStorage.setItem(notifiedKey,newVersion);returntrue;}// 在 checkVersion 中判断if(version&&version!==LOCAL_VERSION&&shouldNotify(version)){onUpdate(version);}

2. 一键发版脚本

// package.json scripts{"release:patch":"npm version patch && npm run build","release:minor":"npm version minor && npm run build"}

3. 轮询间隔建议

场景建议间隔
内部管理系统5 ~ 10 分钟
面向 C 端用户15 ~ 30 分钟
切回页面时立即检测(visibilitychange

九、方案对比

方案优点缺点
version.json 轮询(推荐)实现简单、框架无关、可控需额外请求;依赖部署缓存策略
轮询 index.html 解析 hash无需额外文件解析复杂、请求体积大
Service Worker可静默更新实现成本高,需 PWA 支持

十、常见问题

问题原因解决
发版后用户无感知version.json 被 CDN/Nginx 缓存配置 no-cache
fetch 404publicPath 为./却用绝对路径/version.json改用BASE_URL拼接
开发环境也弹窗未判断环境NODE_ENV === 'production'判断
多标签重复弹窗各 Tab 独立检测localStorage 记录已提示版本
刷新后仍是旧版index.html 被缓存index.html 配置 no-cache

十一、最小实现清单

✅ package.json 维护 version 字段 ✅ 构建配置注入版本号(Vue2: VUE_APP_VERSION / Vue3: __APP_VERSION__) ✅ 构建产物生成 version.json ✅ 运行时通过 BASE_URL 轮询对比 + 更新提示 ✅ 仅生产环境启用检测 ✅ Nginx/CDN 对 version.json、index.html 禁用强缓存

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询