1、问题发现
某天发布测试版本后,测试小伙伴,反馈部分按钮点击后没有相关的交互动作,需要刷新后才能正常使用,经常在刚发版本后出现。刚收到问题的时候,第一反应就是缓存问题造成的,那就刷新一下就好了,但经常在发布版本后出现,无论对测试和用户都不能接受该处理方式,所以来研究一下
2、问题分析
项目采用vue(全家桶)+element-ui+webpack+axios搭建,使用vue-router的懒加载,实现按需加载,发版本后无法访问,应该和webpack打包与vue-router懒加载有关。在webpack配置文件webpack.prod.conf.js,查看相关的输出配置
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[hash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
filename是对应于entry里面生成出来的文件名,chunkFilename未被列在entry中,但有些场景需要被打包出来的文件命名配置。比如按需加载(异步)模块的时候,这样的文件是没有被列在entry中的使用CommonJS的方式异步加载模块。
由于项目是异步加载,打包导出问文件也是chunkFilename,从webpack配置文件可以看出,当修改某个文件的时候会修改该chunkhash的值,相关的js修改,用户点击按钮的时候,通过按需加载请求不到上一个版本的js文件,导致页面卡死,无法应答
3、问题解决
1、监听相关js资源请求
使用addEventListener,实现错误监听
window.addEventListener('error', handleListenerError, true);
* 该监听应放在index.html或项目打包页面的最上面,否则可能无法监听到相关的js请求失败的情况
2、判断相关错误相关信息
1)、判断相关的标签是否是script,eventErr.srcElement.localName == ‘script’
2)、判断是否是自己的按需加载模块的js,eventErr.srcElement.src.indexOf(’/* 域名*/.com/static/js/’);
3)、剔除.js.map的报错,fileName.indexOf(’.js.map’) < 0
3、存储并记录相关信息(防止页面重复刷新)
let reloadObj = {
hash: hash,
time: new Date().getTime()
}
let reloadObjlocalStorage = window.localStorage.getItem('reloadObj');
if(reloadObjlocalStorage == null){
//第一次进来刷新页面
//存储相关信息
window.localStorage.setItem('reloadObj',JSON.stringify(reloadObj));
//刷新界面
window.location.reload();
}else{
//第二次进来,判断上一次信息(理论上不应该进来,除非某些特殊情况,例如因某些原因导致部分js文件丢失),防止页面无休止的刷新
let preReloadObj = JSON.parse(reloadObjlocalStorage);
let time = new Date().getTime();
//两次js文件加载报错时间间隔600毫秒才刷新
if(time > Number(preReloadObj.time) + 600){
window.localStorage.setItem('reloadObj',JSON.stringify(reloadObj))
window.location.reload();
}
}
4、防止中途断网导致页面刷新问题
if(navigator.onLine){ //返回Boolean类型,在线:true,离线:false
window.addEventListener('error', handleListenerError, true);
}
5、完整代码
if(navigator.onLine){
window.addEventListener('error', handleListenerError, true);
}
//监听事件
function handleListenerError (eventErr){
if (eventErr.srcElement.localName == 'script' &&
eventErr.srcElement.src.indexOf('/*域名*//static/js/')) {
let arr = eventErr.srcElement.src.split('/static/js/');
let fileName = arr[1];
let hash = fileName.split('.')[1]; //获取hash
if(fileName.indexOf('.js.map')<0){
let reloadObj = {
hash: hash,
time: new Date().getTime()
}
let reloadObjlocalStorage = window.localStorage.getItem('reloadObj');
if(reloadObjlocalStorage == null){
window.localStorage.setItem('reloadObj',JSON.stringify(reloadObj))
//刷新界面,获取新的html文件
window.location.reload();
}else{
let preReloadObj = JSON.parse(reloadObjlocalStorage);
let time = new Date().getTime();
if(time > Number(preReloadObj.time) + 60){
window.localStorage.setItem('reloadObj',JSON.stringify(reloadObj))
window.location.reload();
}
}
}
}
eventErr.preventDefault()
}
6.刷新页面的作用
html文件中主要有三个js文件:
manifest.[chunkhash].js:在vendor的基础上,再抽取出要经常变动的部分,比如关于异步加载js模块部分的内容。
vendor.[chunkhash].js:通过提取公共模块插件来提取的代码块(webpack本身带的模块化代码部分)
app.[chunkhash].js: 入口js
刷新页面获取新的html,新的html请求上面新的js文件,在新的manifest.[chunkhash].js中包含了最新的异步加载模块的新地址(即js的新路径);
新的html也会包含新的app.[chunkhash].css以便更新新的样式

本文分析了Vue项目中使用懒加载模块在更新版本后出现请求失败的问题,详细介绍了问题的原因在于webpack打包时生成的chunkhash变化导致旧版本的js文件无法加载。文章提供了监听js资源请求错误、判断错误信息、存储并记录相关信息以及防止断网导致的页面刷新问题的解决方案。
1044

被折叠的 条评论
为什么被折叠?



