React+Vite部署Cloud Run后每次更新出现脚本加载失败问题求助
问题现象
使用Vite构建React应用,通过Github Actions自动部署到Cloud Run后,每次新版本上线,带随机哈希的脚本文件(如index-XXX.js)会更新,但之前访问过应用的浏览器会尝试加载旧的哈希脚本,引发两类错误:
Expected a Javascript module script but the server responded with a MIME type of "text/html"- 提示无法找到已不存在的旧脚本文件
硬刷新(F5/Cmd+R)可临时解决,但用户体验差,需要永久修复。
问题根源
核心原因是浏览器缓存了旧的index.html文件:
Vite生产构建会给静态资源添加内容哈希,资源更新后哈希自动变更,但作为入口的index.html没有哈希标识,浏览器会默认缓存它。当新版本部署后,旧的index.html仍会引导浏览器请求已被删除的旧哈希脚本,Nginx返回404页面(HTML类型),从而触发MIME类型错误;或直接返回404找不到文件。
解决方案
1. 配置Nginx缓存策略(关键)
修改nginx.conf,对index.html设置禁止缓存,对带哈希的静态资源设置长期缓存:
server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; # 对带哈希的静态资源设置长期缓存(内容不变则永久有效) location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico)$ { expires 1y; add_header Cache-Control "public, immutable"; try_files $uri =404; } # 强制index.html不被缓存,每次请求都拉取最新版本 location = /index.html { expires -1; add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate"; } # SPA路由 fallback,避免单页应用刷新出现404 location / { try_files $uri $uri/ /index.html; } }
2. 确认Vite构建配置有效性
当前vite.config.ts无需额外修改:Vite默认在生产构建时会给静态资源添加内容哈希,现有manualChunks配置仅影响代码分割逻辑,不影响哈希生成,保持当前配置即可。
3. Cloud Run部署补充
确保每次部署使用唯一镜像标签(当前Github Actions已用$GITHUB_SHA作为标签,满足要求),保证Cloud Run部署全新实例。同时,Nginx设置的Cache-Control响应头会被Cloud Run传递给客户端,无需额外配置Cloud Run的缓存规则。
4. 进阶:添加版本主动检测(可选)
若想主动提示用户应用更新,可在应用中加入版本检测逻辑:
- 步骤1:注入版本号到应用
修改vite.config.ts,将package.json中的版本号注入应用:
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import svgr from 'vite-plugin-svgr'; import pkg from './package.json'; export default defineConfig({ define: { '__APP_VERSION__': JSON.stringify(pkg.version), }, // 其他原有配置... });
- 步骤2:生成版本检测文件
修改Dockerfile的build阶段,在dist目录生成version.json:
# BUILD阶段末尾添加 RUN node -e "const pkg = require('./package.json'); require('fs').writeFileSync('./dist/version.json', JSON.stringify({version: pkg.version}));"
- 步骤3:应用内检测版本
在根组件中添加版本检测逻辑:
import { useEffect } from 'react'; function App() { useEffect(() => { async function checkVersion() { try { const res = await fetch('/version.json'); const data = await res.json(); if (data.version !== __APP_VERSION__) { alert('应用已更新,请刷新页面'); // 也可自动刷新:window.location.reload(true); } } catch (e) { console.error('版本检测失败', e); } } checkVersion(); // 每小时检测一次版本 const interval = setInterval(checkVersion, 3600000); return () => clearInterval(interval); }, []); return <div>你的应用内容</div>; }
总结
通过Nginx配置正确的缓存策略是解决问题的核心,确保index.html不被缓存、哈希资源长期缓存即可彻底解决加载错误。进阶的版本检测机制可进一步提升用户体验。
内容的提问来源于stack exchange,提问作者Antoine Awaida

