HTML自动检测网页更新与缓存控制
在前端开发中,页面更新后用户仍在使用旧版本的情况十分常见。浏览器不会主动提示新版上线,因此开发者需要自行添加检测机制,识别HTML内容的变化。 最直接的方案是轮询 index.html,比对当前页面与服务器最新版本的 script 标签路径是否一致。尤其是Webpack、Vite这类打包工具,JS文
在前端开发中,页面更新后用户仍在使用旧版本的情况十分常见。浏览器不会主动提示新版上线,因此开发者需要自行添加检测机制,识别HTML内容的变化。

最直接的方案是轮询 index.html,比对当前页面与服务器最新版本的 script 标签路径是否一致。尤其是Webpack、Vite这类打包工具,JS文件名通常带hash值——只要脚本路径发生变化,基本意味着新版已发布。具体操作时,每次fetch请求可附带时间戳参数(如 /timestamp=1744574220345),防止浏览器或CDN缓存;拿到HTML字符串后使用 DOMParser 解析,再通过 querySelectorAll('script[src]') 提取所有带src属性的脚本路径;将这些路径存入 Set 进行差集比较,比全文比对更稳定(避免注释、空格、换行等干扰)。轮询间隔建议设为5到10分钟,太短浪费请求,太长则可能错过关键更新。
fetch('/t=xxx') 轮询 index.html 是最直接的方案
核心是比对当前页面 标签的 src 是否与服务器最新HTML中的不一致。Webpack/Vite打包后JS文件名带hash,只要脚本路径变了,基本代表新版本。
- 每次请求加时间戳(如
/timestamp=1744574220345)防止被浏览器或CDN缓存 - 用
DOMParser解析返回的HTML字符串,再用querySelectorAll('script[src]')提取所有带src的脚本路径 - 将路径转为
Set后做差集比较,比字符串全文比对更稳定(避免注释、空格、换行干扰) - 轮询间隔建议设为5–10分钟;太短(如2秒)会浪费请求,太长(如1小时)可能错过关键更新
别信 ETag / Last-Modified,它们常被服务端逻辑拦住
很多Nginx或Node.js中间层默认开启协商缓存,但返回 304 Not Modified 时,前端无法收到新内容,自然无法触发更新判断。这恰是常见踩坑点。
- 检查Network面板中HTML请求是否真返回了
200和新响应体;若看到304,说明服务端未放行 - 强制禁用协商缓存:fetch时加
cache: 'no-cache',并手动添加请求头Cache-Control: no-cache - 某些CDN(如Cloudflare)会忽略客户端Cache-Control,需在后台关闭“智能缓存”,或为
/index.html单独配置缓存策略为no-store
页面不可见时要暂停轮询,否则白跑还耗电
用户切换标签页、最小化窗口、锁屏时继续发请求,既无意义又影响性能,尤其在移动端会明显增加耗电。这一优化常被团队忽略。
- 监听
document.hidden变化,配合visibilitychange事件控制定时器启停 - 恢复可见时,不要立刻重试,先延后1–2秒再fetch,避免与页面初始化抢资源
- 不要依赖
setInterval硬轮询;改用setTimeout链式调用,方便随时中断和重置
swapCache() + reload(true) 是过时写法,别再用了
window.applicationCache 在Chrome 95+、Firefox 92+ 已彻底移除,且它只适用于老旧的 manifest 方案,与现代打包部署完全不兼容。
- 现在所谓“更新”,本质是HTML文件内容变了,不是缓存清单变了
- 检测到差异后,弹窗提示比自动刷新更稳妥;
window.location.reload(true)会丢失表单输入、滚动位置、WebSocket连接等上下文 - 如非要自动刷新,至少加一个延迟(如30秒倒计时),并允许用户点击按钮提前触发
说到底,真正困难的并非那几行fetch代码,而是让检测逻辑在各种网络环境、CDN配置、浏览器休眠策略下都能可靠运行。尤其是看似无害的 304 响应,最容易被忽略,也最常导致“明明发版了,用户就是不更新”的尴尬局面。
