在前端开发中,我们通常会使用打包工具(如 Vite、Webpack)将 JS/CSS 等资源构建上线,并配合 CDN、浏览器缓存来加速访问。但问题也随之而来:
"我已经发布新版本了,怎么用户还看到旧界面?"
"为啥修复的 bug 还在?"
原因很简单:用户浏览器缓存了旧的静态资源,没有感知新版本的变更。

🎯 目标:前端页面如何自动检测打包更新并提示刷新
关键需求是:
- 服务器发布了新版本,客户端能"感知"到。
- 最好不依赖后端数据库或额外接口。
- 弹窗或强制刷新,提示用户更新。
思考:为啥不直接加 hash?
其实我们打包时已经使用 [hash]
命名资源了,那为啥还要检测更新?
因为:
- 浏览器对 HTML 本身不会缓存 bust;
- 页面里写死了旧的
<script src="/main.abcd.js">
,不会自动拉新版; - 用户长时间不刷新页面就不会拉取新 HTML,自然也没拉到新版 JS。
所以必须在 运行中的前端代码层面 进行更新感知。
方案一:轮询版本文件(最常用)
实现思路:
- 打包时生成一个版本文件(如
version.json
或注入index.html
里的__BUILD_VERSION__
字段); - 页面加载后,定时轮询该文件;
- 一旦检测到版本变更(比如哈希不同),提示用户刷新。
示例代码:
typescript
const CURRENT_VERSION = __BUILD_VERSION__; // 构建时注入变量
setInterval(() => {
fetch('/version.json?t=' + Date.now())
.then(res => res.json())
.then(({ version }) => {
if (version !== CURRENT_VERSION) {
alert('检测到新版本,点击确定刷新页面');
window.location.reload(true);
}
});
}, 10000); // 每 10 秒轮询一次
或者使用文件指纹思路:
-
打包后生成一个 manifest 文件 (比如
asset-manifest.json
或meta.json
),里面记录资源及其 hash; -
页面运行时轮询这个 manifest 文件;
-
如果发现文件名或 hash 有变,提示刷新页面。
在 Vite 中添加如下配置:
typescript
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
build: {
manifest: true, // 开启 manifest 生成
}
})
构建后会生成 dist/manifest.json
,类似这样:
typescript
{
"index.html": { "file": "index.html", "isEntry": true },
"src/main.ts": { "file": "assets/main.abc123.js", "isEntry": true },
"src/style.css": { "file": "assets/style.def456.css" }
}
前端代码(可封装成检测模块):
typescript
let currentVersionHash = ''
function getHashFromManifest(manifest: any): string {
// 自定义 hash 生成方式:可拼接所有文件路径,也可只取主入口
return Object.values(manifest)
.map((entry: any) => entry.file)
.join('|')
}
async function checkForUpdate() {
try {
const res = await fetch(`/manifest.json?_t=${Date.now()}`)
const manifest = await res.json()
const newHash = getHashFromManifest(manifest)
if (!currentVersionHash) {
currentVersionHash = newHash
} else if (currentVersionHash !== newHash) {
console.log('🚨 发现新版本,准备刷新页面')
alert('检测到新版本,点击确认刷新页面')
window.location.reload(true)
}
} catch (err) {
console.error('版本检查失败', err)
}
}
setInterval(checkForUpdate, 10000) // 每 5 秒检查一次
💡
manifest.json
中的文件路径含 hash,只要内容变,hash 就会变,就能准确识别版本是否更新。
优点:
- 实现简单,支持所有现代浏览器;
- 可手动控制提示逻辑,提升用户体验;
- 和打包工具(Vite/Webpack)配合紧密。
缺点:
- 本质上还是轮询,数据拉取有频率;
- 如果用户挂着页面一整天,刷新前所有交互还是旧代码。
方案二:WebSocket(配合 CI/CD 或打包系统)
✅ 前提:
- 服务端拥有 WebSocket 服务能力(如 Node.js +
ws
模块); - 每次前端构建部署完成后,触发一次"更新通知"。
服务端(nodejs+ws模块):
js
// ws-server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
const clients = new Set();
wss.on('connection', (ws) => {
console.log('🚀 New client connected');
clients.add(ws);
ws.on('close', () => {
clients.delete(ws);
});
});
// 通知所有客户端刷新页面
function notifyClients() {
for (let client of clients) {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({ type: 'update', message: 'new-version' }));
}
}
}
// 模拟触发更新(实际应在构建完成后调用)
setTimeout(() => {
console.log('🎉 New version released, notifying clients...');
notifyClients();
}, 10000); // 10 秒后触发
如果你有配套的部署平台或打包平台(如 Jenkins、Vercel、Netlify),可以在部署成功后广播一条"版本已更新"的消息。
客户端建立 WebSocket 连接,一旦收到推送,就提示刷新页面。
js
if ('WebSocket' in window) {
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = () => {
console.log('[WS] Connected to update server');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'update') {
console.log('[WS] New version detected');
// 这里可以是弹窗提醒,或者直接刷新
if (confirm('检测到新版本,是否立即刷新页面?')) {
window.location.reload();
}
}
};
ws.onclose = () => {
console.log('[WS] Connection closed');
};
ws.onerror = (err) => {
console.error('[WS] Error:', err);
};
}
可封装为插件或独立模块进行复用,比如 Vue 插件、React Hook、Vite 插件等。
优点:
- 实时;
- 可以配合更多更新逻辑(如强制下线);
缺点:
- 成本高,依赖后端支持,对于检测更新的需求一般无需这么实时;
- 不适合资源有限的小项目;
总结
前端页面检测更新并不是"有没有后端通知",而是我们主动地检测自己的版本是否已经被 CDN 或打包平台更新 。 轮询 version.json
是最稳定、兼容性最强的方案; WebSocket 通知 是顶配方案,适合重型系统。