一、现象描述
项目线上测试时发现(本地测试未出现该问题):长时间停留在某一个页面,切换菜单成功后,但是页面还是展示的之前的,且路由未跳转;手动刷新页面后,正常。
二、报错提示
项目在本地开发没问题,但是打包到测试环境时发现:页面无操作停留一段时间(半小时+)后,页面跳转失效,查看控制台报错。
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.
网页中使用了模块脚本(module script),但服务器返回的 MIME 类型是 “text/html”,而不是 JavaScript 类型。根据 HTML 规范,严格的 MIME 类型检查会对模块脚本进行强制执行。
Uncaught (in promise) TypeError: Failed to fetch dynamically imported module: http://xx.x.x.xx:xxxx/assets/Index-1bd9e109.js
三、问题原因
上线打包的自动化工具会把上一个版本的资源文件清空,但是vite打包发版后,当前页面缓存的还是发版之前的资源,未主动刷新成新资源,导致跳转页面时在服务器上找不到之前的包,所以出现这种问题。主动刷新后,页面缓存资源更新,跳转正常。
使用vite对项目进行打包,对 js 和 css 文件使用了 chunkhash 进行了文件缓存控制,但是项目的index.html文件在版本频繁迭代更新时,会存在被浏览器缓存的情况。
在发版后,如果用户不强制刷新页面,浏览器会使用旧的index.html文件,在跳转页面时会向服务器端请求了上个版本 chunkhash 的 js 和 css 文件,但此时的文件已经在版本更新时已替换删除了,最终表现为页面卡顿,控制台报错 等。
在 vue-router 里使用了页面模块懒加载,通过 Vite 打包之后,不同于 Webpack ,生成的 dist/assite 文件夹中,会有很多个小的 js 文件,而 Webpack 打包会生成一个大的 app.js 文件。这样的好处是,大大提升了项目首页开屏的速度!
但是缺点上完线之后就暴露出来了,由于上线打包,使用自动化工具,会把之前的项目文件清空,然后把 build 之后的文件移动过来,相当于整个做了一个替换!
所以如果客户端不主动刷新浏览器,用户的本地项目还是上一个版本的,当它需要进行跳转页面或者引入其他的包时,自然在服务器上找不到之前的包了
四、解决思路
方法一:服务器发版时,上一个版本的asset文件包不删除(不合理)
缺点:
- 随着频繁发版,服务器端前端项目文件会越来越多,浪费空间;
- 若旧页面的接口涉及到参数改动等,会引起报错;
- 流水线使用 docker 打包部署会变得非常麻烦。
方法二:前后端配合,定义一个版本号接口
类似 App 版本更新,每次上线都要定义一个版本号,通过后端接口进行修改和获取版本号,需要前端配合。
缺点:
- 实现起来相对来说较麻烦;
- 需要一定时间;
方法三:前端记录比对版本号(方法二简化版)(推荐)
前端打包时,自己生成一个json文件来记录本次打包的版本号,路由跳转的时候,通过请求服务端版本号与本地版本号是否一致,决定是否刷新页面。
ps:
- 版本号唯一:随机码,uaaId 、随机数、时间戳都可以
方法四:记录路由跳转时间,定时刷新
直接通过记录页面跳转的时间,来判断是否刷新,
缺点:
- 刷新时间可能不够及时,导致发版时间与刷新时间刚好错开;
- 消耗性能;
方法五:退出登录的时候,刷新页面
方法六:捕捉跳转错误,刷新页面(推荐)
五、解决方法
方法三具体代码如下
如何解决前端上线之后用户页面不刷新的问题_前端打包hash文件后页面需要刷新问题-CSDN博客文章浏览阅读4.4k次,点赞5次,收藏17次。这种场景就好像是 App 版本更新似的,每次上线都要定义一个版本,这个版本通过后端接口进行修改和获取,但是需要前端和后端一起配合,以及一整套流程控制,数据表维护等。实现起来相对来说较麻烦,需要一定时间。..._前端打包hash文件后页面需要刷新问题
https://blog.csdn.net/Kevinblant/article/details/126237531
方法六具体代码如下
import { useRouter } from "vue-router";
const router = useRouter();
// 点击菜单时,捕捉报错,刷新页面
router.onError(error => {
if (error.message.includes("Failed to fetch dynamically imported module")) {
// reload():类似于点击浏览器上的刷新页面按钮
window.location.reload();
}
});
参考链接:【已解决】Vite 部署后报错:Failed to fetch dynamically imported module_前端知识库
六、其他
辅助理解链接
1、【该方法没有试过,与上述“五、解决方法”类似】
Vue3+vite前端项目打包升级发版后,不手动刷新,页面跳转文件找不到 - 简书
2、Nginx通过反向代理做负载均衡【该方法没有试过】
vite 打包上线后页面静止一段时间(半小时+)路由跳转无响应_vite 打包上线后页面静止一段时间(半小时+)路由跳转无响应-CSDN博客
3、vue3+vite打包以后,项目切换路由触发(偶发触发)报错【该方法试过,没有生效】