H5端移动端旧资源问题根源在于index.html未刷新,即使JS/CSS带contenthash也无效;需服务端设Cache-Control: no-cache并配合客户端时间戳动态加载或URL参数强制更新。

直接结论:H5端在移动端(尤其是微信、钉钉、iOS Safari)出现旧资源,核心矛盾不是“要不要缓存”,而是 index.html 没被刷新 —— 它一缓存,后面所有 js、css 即使带了 hash 也白搭。
为什么加了 [contenthash] 还是加载旧 JS?
因为浏览器先加载 index.html,而它通常没做版本控制。一旦 index.html 被缓存(比如响应头里 Cache-Control: public, max-age=3600),后续所有资源引用都基于这个“老 HTML”里的路径,哪怕新打包的 app.abc123.js 已上线,用户根本不会去请求它。
- 微信内置浏览器对
meta标签禁用缓存的支持极不稳定,<meta http-equiv="Cache-Control" content="no-cache">基本无效 -
location.reload()在页面内调用,可能触发白屏或二次加载,且无法保证index.html真的重新拉取(尤其 iOS 微信) - 服务端设置
Cache-Control: no-cache对index.html有效,但必须确保 CDN 或 Nginx 没覆盖该头
vue.config.js 中正确配置文件名 hash + 时间戳
只对 js 和 css 加 [contenthash] 不够,H5 平台要额外加构建时间戳,避免因内容未变导致 hash 不变 —— 这在热更新或 CI/CD 频繁发版时极易踩坑。
- 必须限定仅在 H5 平台生效:
process.env.UNI_PLATFORM === 'h5' -
filename和chunkFilename都要改,否则异步 chunk(如路由组件)仍走旧名 - 时间戳建议用
new Date().getTime(),不用Math.random()(会导致每次 dev 启动 hash 变,影响 HMR) - 示例配置片段:
configureWebpack: (config) => {
if (process.env.UNI_PLATFORM === 'h5') {
const timestamp = '.' + new Date().getTime()
config.output.filename = `static/js/[name].[contenthash:8]${timestamp}.js`
config.output.chunkFilename = `static/js/[name].[contenthash:8]${timestamp}.js`
}
}
强制刷新 index.html 的实操方案
不能依赖 meta 或 reload,得让浏览器“认为 URL 变了”。最稳的方式是服务端配合 + 客户端兜底。
- 服务端:把
index.html的响应头设为Cache-Control: no-cache, no-store, must-revalidate,并确保ETag或Last-Modified正确生成(Nginx 示例:add_header Cache-Control "no-cache, no-store, must-revalidate";) - 客户端兜底:在
index.html的里插入脚本,用时间戳动态加载 JS(绕过 HTML 缓存):
<script>
const script = document.createElement('script')
script.src = '/static/js/app.' + Date.now() + '.js'
document.head.appendChild(script)
</script>
- 注意:此方式需确保打包后 JS 路径可预测,且不能和 webpack 的 runtime 冲突;更稳妥的做法是修改
uni-app的index.html模板,在index.html文件本身中注入时间戳(通过html-webpack-plugin的templateParameters)
微信/钉钉环境下的特殊处理
这些容器 WebView 对标准缓存策略有定制行为,单纯前端手段容易失效。
- 微信:必须在公众号后台“网页授权域名”下,且 URL 后带
?v=xxx才能稳定触发重拉(v值建议用 Git commit hash 或构建时间戳) - 钉钉:自建 H5 应用需在
dd.config后主动调用dd.ready(() => { location.reload() }),但仅限首次进入;更可靠的是在dd.runtime.permission.requestAuthCode回调里判断是否需刷新 - iOS Safari:对
Cache-Control解析较严格,max-age=0不等于不缓存,必须配no-store才真正跳过磁盘缓存
真正难的不是加参数或改配置,而是确认哪一层缓存没被破 —— 先用 Chrome DevTools 的 Network 面板看 index.html 的响应头和 Status(是 200 还是 304),再查 JS 请求的 URL 是否带了预期的时间戳。漏掉任何一环,问题都会复现。


















