type
status
date
slug
summary
tags
category
icon
password
😀
今天介绍一下在使用webpack构建的项目,出现ChunkLoadError:Loading chunk x failed.报错时的一种解决方案
 
notion image
这个报错并不是资源请求失败,控制台并没有出现Network Erro这种请求失败的问题,而是资源模块挂载失败,可能是客户缓存了错误的资源导致。
这个报错是从webpack内部抛出的,代码位置是
这个文件是webpack内部加载 chunk 资源的时候,判断资源是否正常加载,整体机制是
  1. webpack入口文件维护一份chunk资源的path以及其对应的资源号的列表(这个资源号也会编码在对应 chunk中)
  1. 遍历资源列表逐一加载,每次加载都会在页面中手动插入一段script脚本, src 赋值为资源地址,并挂载 loaderror 两个事件的回调函数
  1. chunk脚本内部的入口会执行一段代码,访问 window 中的全局变量,并将当前 chunk 中写死的模块号存入全局变量
  1. onload 会校验当前资源的模块号是否存在与全局的缓存中,存在则判定chunk加载成功,否则加载失败,抛出异常
下面这段代码用于抛出异常,异常的message由下面这段代码拼接:
注意其中的 errorType 字段,这个字段的取值有两种情况,如果 event.type === 'load'errorType = 'missing', 否则就赋值为 event.type
所以如果错误中出现了 missing 的字样,就说明 event.type='load',也就是这个脚本实际是已经被正常加载了,但是出于某种原因,他没有通过资源编号的检测。
 
是什么原因呢?
 
这里我代入了自己的一些主观猜测,这个报错主要是为了上报模块加载中的异常情况,如果资源加载成功,但是资源编号检查失败,是不是说明这段 chunk 本身有问题呢?
  1. chunk加载了, 但是没有正常执行
  1. chunk执行了,但是里面的内容有问题,导致模块号挂载失败
第一种情况,可能是chunk内部有异常
第二种情况,我理解为chunk对应的地址存在浏览器缓存,在客户网络波动时没有完全加载完成,结果这份不完整的资源被缓存了起来,后续一直在使用错误的内容,比如客户拿到的资源中的模块号错误。
有了这个想法我找了一个项目进行尝试,Chrome代理返回的脚本内容,修改第一行的模块号,刷新页面后再次点击地区就会出现missing错误, 完美复现!
notion image
所以最终的解决方案,就是找到异常的 chunk ,强制刷新客户的缓存
  • 粗糙的方案:直接拿到 chunk 地址,写一段脚本放在html中让客户访问
url-cache-flush
CreateSunUpdated Dec 30, 2024
我提供了一个清除缓存的工具供客户使用,显示 “clear success!” 即表示缓存清除成功。
其实源码非常简单,就是用原生的 fetch 方法对目标chunk对地址发起请求,请求头中声明 "Cache-Control": "no-cache" 即可。
  • 优雅的方案:在代码中捕获异常,并进行自动重试
这种方式可以考虑在比如动态加载的入口,比如import语句、React.lazy外部增加catch捕获异常并解析目标地址,具体的实现方式就不展示了。
 
💡
有关类似的 webpack资源加载问题,欢迎您在github留言,我们一起交流