新闻详情

新闻详情

首页 / 资讯中心 / 详情

ChunkLoadError自动恢复:前端chunk失败生产方案

发布时间:2026/10/1 1:34:01来源:尧图网络
ChunkLoadError自动恢复:前端chunk失败生产方案
凌晨两点手机上的监控告警群连着弹出十几条同样文案的消息Loading chunk 18 failed。打开大盘一看受影响用户占比 3.7%机型集中在 Android 微信内置浏览器时间点卡在我们上一版发布后的第 40 分钟。这种场景只要做过两年前端的人大概率都遇到过——Loading chunk {n} failed这行字几乎是所有用了动态导入、路由懒加载、按需分包项目的老熟人。它不难理解但难在它每一次出现的原因都不太一样而国内绝大多数团队的处理方式还停留在让用户清缓存刷新一下这在生产环境里基本等于放弃治疗。这篇内容我想干三件事把这行报错从浏览器到构建产物的整条链路拆开讲清楚给出一套我实际在项目里跑了一年多、能上生产的自动恢复方案最后把那些名字也叫 chunk failed 但其实跟前端半毛钱关系没有的报错区分开——包括最近热词里那条数据侧的transmit chunk rpc failed很多人搜同一个关键词被带到完全错误的方向上。适合正在被这个问题折磨的前端、也适合做发布流程和 CDN 配置的运维同学对着看。1. 先把这条报错定位清楚它到底是谁抛出来的1.1 浏览器控制台里那句红字其实来自 webpack runtime很多人第一反应是浏览器报的错其实不是。浏览器原生只会告诉你某个 URL 请求失败了而Loading chunk 18 failed这句话是构建产物里的 runtime 代码在script标签onerror之后自己拼出来并抛出的。webpack 4 时代__webpack_require__.e负责加载异步 chunk它的实现大致是创建一个script标签src指向__webpack_require__.p jsonpScriptSrc(chunkId)然后在onerror里 reject 一个new Error(Loading chunk chunkId failed.)。所以这句话的完整含义是我去请求 chunk 18 对应的那个 JS 文件请求失败了——注意是请求失败不是文件不存在。404、超时、DNS 失败、被拦截、服务器返回了 HTML全都会走到这个分支。webpack 5 做了一点改进错误对象上多了几个有用的字段error.name ChunkLoadErrorerror.type记录了失败类型timeout、error、missing等error.request是真实请求的 URL。这几个字段在我看来是排查效率的分水岭因为error.request能直接告诉你当时请求的是哪个带 hash 的文件名。有了它你只需要把这个 URL 拿去比对一下当前线上发布的 manifest几秒钟就能判断出是版本错位还是网络抖动。提示如果你在 Sentry、自建监控里看到的 chunk 错误没有request字段多半是因为监控 SDK 只采集了message没采集自定义属性。改造采集逻辑比在业务代码里到处打日志划算得多。1.2 报错文案的几种变体与它们各自的上游同一个根因会因为构建工具和插件不同长出七八种不同的文案。我整理了一张对照表建议把它贴在团队的排查文档里省得每次都要重新查报错文案出自真实含义Loading chunk 18 failed.webpack 4 / 5JS chunk 的 script 请求失败Loading chunk 18 failed. (missing: 20 21)webpack 5不只 18依赖链上的 20、21 也没加载上Loading CSS chunk 5 failed.mini-css-extract-pluginCSS chunk 请求失败通常是 link 标签加载失败ChunkLoadErrorwebpack 5 的 error.name上面那几种的统一标识抓监控时用这个最省事Failed to fetch dynamically imported moduleVite / Rollup动态 import 的模块请求失败语义等价Unable to preload CSS for …Vite预加载 CSS 失败常和上面的错一起出现MIME type (text/html) is not a supported stylesheet浏览器请求 JS/CSS 却拿到了 index.html多半是路径回退最后一条特别值得说。它看起来是 MIME 类型问题实际上九成是因为服务器把所有找不到的路径都 rewrite 到了index.htmlSPA 的常规配置于是浏览器请求一个已经不存在的/static/js/18.a1b2c3.js服务器很客气地返回了首页 HTML状态码还是 200。这种情况下onerror反而不会触发但浏览器会因为 MIME 不匹配拒绝执行最终表现依然是页面白屏加一堆 chunk 报错。排查时如果看到 200 状态码但页面还是挂了先去看响应体是不是 HTML。1.3 一个心法所有 chunk 失败只有两种底色排查到最后你会发现根因都能归到两个方向里资源无法被找到发布删了旧文件、CDN 缓存错位、publicPath 配错、CI 产物不一致。资源无法被取回弱网超时、离线、被扩展拦截、跨域配置缺失、服务端限流。这两类的处理策略完全不同。第一类重试毫无意义因为那个 URL 在服务端已经不存在了你重试一百次也是 404正确的做法是让页面重新加载、拿到新的资源清单。第二类重试是有效的网络抖动一秒后可能就通了。我在项目里最终落地的方案就是先做这个二分判断再决定走哪条恢复路径误判率比一律刷新低很多用户体感也好得多。2. 报错文案背后的四种真实场景2.1 发版即失联旧页面追着新清单跑这是占比最高的一种我估过我们项目的线上数据能占到七成以上。典型的触发链路是这样用户下午三点打开页面浏览器把index.html和当时的app.aaa111.js、18.bbb222.js一起下载并缓存下午四点我们发了一版18这个 chunk 的内容变了hash 变成ccc333旧的bbb222被构建产物覆盖删掉用户还在那个标签页里刷着五点他点了一个路由跳转runtime 拿着内存里记着的bbb222去请求服务器上早就没有了404报错。这里有个容易被忽略的细节用户不需要停留在旧页面很久才会中招。只要他在你发布的那一瞬间正好开着页面哪怕只开了三分钟也一样会踩到。尤其是持续挂着后台的 B 端系统、客服工作台、监控大屏这些页面一开就是一整天每次发版都会有一批人报错。更隐蔽的是半个身子换过去了的情况。假设一个页面同时用了三个 chunk用户请求时前两个还在、第三个已经被新版本替换那么他会看到一个部分渲染、部分空白的中间态而不是干净的白屏。这种半死不活的状态最难排查因为错误堆栈看起来毫无规律。注意判断是不是这个原因最快的办法是拿用户上报的error.request里的文件名去比对当前线上index.html里的资源引用。对不上就是版本错位直接结案。2.2 灰度与 CDN 的缓存错位如果说第一种是新老版本互相打架这一种就是同一版本的资源在 CDN 上打架。常见于三种操作一是灰度发布按机器分批。十台机器先发两台用户的请求经过负载均衡第一次拿到新版本的index.html第二次落到老机器上取 chunk文件名对不上报错。这种问题的特点是发版期间错误率上升发完自动消失很容易被当成偶发噪音忽略掉。二是CDN 缓存刷新不及时。你更新了index.html但边缘节点还缓存着旧的因为没配no-cache用户拿到旧 HTML里面的 chunk 名字当然是旧的。这个和第一种现象一样但根因在缓存策略改前端代码是治不好的。三是跨区域回源不一致。这个在小团队少见但做多地域部署的一定遇到过华东节点已经刷新了华南节点还在回源老文件用户漫游或者切网络的时候就会撞上。我在实际项目里判断这类问题有个土办法看错误上报里的客户端 IP 归属地分布。如果错误集中在某一个省份或者某一个运营商基本可以锁定是那一片 CDN 节点的缓存问题直接去刷那个区域就行。2.3 弱网、代理拦截与浏览器扩展这一类跟版本没关系纯粹是请求没走通。移动端地铁里、电梯里、酒店 Wi-Fi 认证页面前用户点了一个大 chunk 的路由请求超时webpack 默认chunkLoadTimeout是 120 秒实际上大多数用户等不到 120 秒就自己关掉了但错误还是上报了。还有两种更微妙的情况。一种是浏览器扩展拦截某些广告拦截、隐私保护插件会把 URL 里带特定关键词的请求直接掐掉表现为net::ERR_BLOCKED_BY_CLIENT。另一种是企业内网的出口代理会因为资源域名不在白名单里而返回一个自定义的错误页。这两种的共同点是只影响特定人群错误率很低但一直不断很容易被误判成偶发。判断方法很简单看同一个用户是不是反复出现。如果某个用户的设备 ID 在一天内报了五六次且分散在不同 chunk 上那基本就是他的网络环境或浏览器环境有问题而不是你的代码有问题。2.4 构建侧自己出问题这一种最少见但一旦出现就非常折磨人因为它会表现为所有人都挂或者某个特定版本必挂。典型的有CI 上开了持久化缓存但缓存的是旧 manifest导致产出的index.html引用的 chunk 名字和实际生成的对不上publicPath配置成了相对路径页面在二级路由下访问时 chunk 请求跑到了错误的目录多入口项目里两个入口共享的 chunk 被错误地切分运行时找不到依赖。还有一种是构建过程中断产出了不完整的dist目录某些 chunk 文件根本没生成。这类问题的特征是本地复现得了。你本地跑一遍npm run build再用静态服务器打开如果在无痕模式下也能稳定复现那就不用怀疑线上环境了老老实实去查构建配置。3. 兜底方案一套能上生产的自动恢复策略说完原因进入正题。用户不会因为这是发版导致的就原谅白屏所以我们必须在代码里做兜底。下面这套方案我在两个 C 端项目和一个 B 端系统上都跑过错误恢复率能到 90% 以上。3.1 监听写在哪、抓什么入口文件最顶部越早越好最好在框架加载之前就注册好。两个事件都要监听const CHUNK_ERROR_RE /Loading chunk [\w-] failed|Loading CSS chunk|ChunkLoadError|Failed to fetch dynamically imported module/; function extractReason(e) { const reason e (e.reason || e.error || e); if (!reason) return null; const msg reason.message || String(reason); if (!CHUNK_ERROR_RE.test(msg)) return null; return { message: msg, request: reason.request || , // webpack 5 才有 type: reason.type || , name: reason.name || , }; } window.addEventListener(unhandledrejection, (e) { const info extractReason(e); if (info) handleChunkError(info); }); window.addEventListener(error, (e) { // 资源加载失败不会冒泡到 window必须用捕获 const info extractReason(e); if (info) handleChunkError(info); }, true);这里有两个坑必须点出来。第一window.addEventListener(error)第三个参数一定要传true否则捕获不到资源加载错误因为资源的 error 事件不冒泡。第二动态import()的失败是以 Promise reject 的形式抛出的走的是unhandledrejection所以两个事件一个都不能少。我最早只写了unhandledrejection结果漏掉了 CSS chunk 和部分 preload 错误监控上看错误量只有实际的六成。3.2 为什么优先 reload 而不是原地重试很多同学的直觉是失败了就再试两次代码写起来也确实优雅。但如果你在第 2 节里跟着看下来了应该已经能想明白对于占比七成的版本错位类失败重试是无效的那个 URL 在服务端已经不存在了。重试三次用户多等三秒最后还是白屏。所以我的策略是分两步走async function handleChunkError(info) { // 第一步记录但先不刷 report(info); // 第二步区分类型 if (info.type timeout || isNetworkLike(info)) { // 网络类原地重试一次间隔 800ms if (await retryOnce()) return; } // 其他情况或者重试也失败走整页刷新 safeReload(); }retryOnce的实现不用太花哨核心就是拿到失败的模块重新 import 一次。这里有个技巧不要试图去 hook webpack 的__webpack_require__.e不同版本内部实现差异很大升级一次 webpack 就可能失效。更稳的做法是在业务层的懒加载封装里做重试这点在第 5 节会展开。至于刷新location.reload()就够了。有人喜欢用location.href location.href效果类似但多了一次历史记录某些浏览器上会影响返回键行为我不太推荐。3.3 防刷新死循环的三道闸location.reload()一旦不加限制就是灾难现场服务器真的挂了用户打开页面 → 报错 → 刷新 → 又报错 → 又刷新页面像抽搐一样不停闪用户手机发烫电量狂掉。我见过最夸张的一个案例是用户手机被刷到没电。我现在固定用三道闸const RELOAD_KEY __chunk_reload_at__; const RELOAD_COUNT_KEY __chunk_reload_count__; function safeReload() { const now Date.now(); const last Number(sessionStorage.getItem(RELOAD_KEY) || 0); const count Number(sessionStorage.getItem(RELOAD_COUNT_KEY) || 0); // 闸一时间窗10 秒内只允许刷一次 if (now - last 10000) return; // 闸二次数上限一个会话最多刷三次 if (count 3) { showFriendlyError(页面资源加载失败请稍后重试); return; } sessionStorage.setItem(RELOAD_KEY, String(now)); sessionStorage.setItem(RELOAD_COUNT_KEY, String(count 1)); location.reload(); }闸三是在刷新前加一个版本校验请求一个带时间戳的/version.json如果发现服务端版本和内存里记录的版本一致说明不是版本问题那就别刷了直接提示用户。这一道能进一步降低无效刷新的比例。用sessionStorage而不是localStorage的原因也很简单——用户关掉标签页重新打开就应该是一次全新的机会会话级存储正好符合这个语义。提示刷新的时候可以用location.reload()但如果你的项目跑在微信内置浏览器或者某些 App 的 WebView 里偶尔会遇到 reload 不生效的情况这时可以退化成location.replace(location.href)。我在两个项目里都加了这层兜底。3.4 上报要做的取舍监控上报不是越多越好。我们踩过的坑是一开始把每一笔 chunk 错误都上报结果发版那半小时的错误量把整个大盘的告警阈值冲爆了值班同学直接关掉了告警反而错过了真正的问题。后来改成三条规则同一用户同一 chunk 五分钟内只报一次用设备 ID chunk ID 做去重键自动恢复成功的降低采样率比如只报 10%因为这类属于可自愈问题不需要人工介入自动恢复失败的 100% 上报这类才是真的影响用户。另外一定要把error.request、页面版本号、navigator.connection.effectiveType一起带上排查效率完全不是一个量级。4. 事前预防把失败窗口压到最小兜底是止血真要少出问题还得靠事前。这块其实比写代码更重要而且很多成本是一次性的。4.1 index.html 与 hash 资源的缓存策略核心原则只有一条入口 HTML 和带 hash 的静态资源要用两套完全相反的缓存策略。# 入口 HTML绝对不能缓存 location /index.html { add_header Cache-Control no-cache, no-store, must-revalidate; add_header Pragma no-cache; etag off; if_modified_since off; } # 带 hash 的静态资源缓存一年都可以 location ~* ^/static/.*\.[0-9a-f]{8,}\.(js|css|woff2|png|svg)$ { expires 1y; add_header Cache-Control public, max-age31536000, immutable; access_log off; }这套配置的道理很直白index.html是资源清单它里面的文件名带 hash内容一变名字就变所以每次都必须拿最新的而 chunk 文件名本身已经带了内容指纹内容不变名字不变缓存再久也不会出错。反过来配的话把index.html也缓存一年那用户一年都拿不到新版本每次发版都是一场灾难。我在项目上还加了一条保险index.html的响应头里带上X-App-Version前端启动时读一次存起来。等到真的出 chunk 错误就能对比当前运行的版本和服务端最新版本判断是不是需要刷新。4.2 多版本共存的发布方式这一条是被最多团队忽略、但性价比最高的措施发布时不要删旧文件保留最近 N 个版本的静态资源。具体做法是在构建产物目录上按版本号分目录比如/static/2024.11.20-1/、/static/2024.11.20-2/index.html里的publicPath指向当前版本目录。发布新版本时旧目录不动。这样即使用户停留在旧页面他的 chunk 请求打到旧目录还能正常拿到文件页面继续正常工作只是他自己不知道有新版本而已。保留多少个版本我的经验是保留最近 5 到 10 个视发布频率而定。日更的项目保留一周的量就够了周更的可以保留一个月。磁盘成本相比用户投诉成本完全不值一提。清理旧版本有两种方式一种是发布脚本里顺手删除超过阈值的目录另一种是给每个版本目录加一个expire-at的元数据文件由定时任务扫描清理。我倾向第二种逻辑更收敛也不容易在发布失败时误删。4.3 构建与网络参数调优几个我实际调过的参数都很小但有用// webpack.config.js module.exports { output: { chunkLoadTimeout: 30000, // 默认 120s移动端建议缩到 30s crossOriginLoading: anonymous, // CDN 跨域时加 crossorigin 属性 }, };chunkLoadTimeout从 120 秒缩到 30 秒的理由是移动端用户根本不会等两分钟与其让他对着白屏发呆不如早点失败早点进恢复流程。crossOriginLoading则是在资源走独立 CDN 域名时必配的否则错误信息会被浏览器屏蔽成一句Script error.什么都查不到。另外prefetch的用法也要注意。给路由级 chunk 加上webpackPrefetch: true确实能让空闲时预取降低点击时的失败率但预取失败的错误也会走到全局监听里如果你不加过滤就会看到一大堆用户还没点过的页面在报 chunk 错误。我的处理是在上报时判断error.request对应的 chunk 是否在当前路由的依赖链上不在的就降级成低优先级日志。4.4 Service Worker 场景下的版本切换如果项目上了 PWA情况会更复杂一层因为 SW 自己有一份缓存。经典的现象是代码已经发布了用户刷新了好几次都还是老页面最后报了一个 chunk 错误。核心是两件事。一是让新 SW 尽快接管self.addEventListener(install, (e) { e.waitUntil(self.skipWaiting()); }); self.addEventListener(activate, (e) { e.waitUntil( (async () { await caches.keys().then((keys) Promise.all(keys.filter((k) k ! CACHE_NAME).map((k) caches.delete(k))) ); await self.clients.claim(); })() ); });二是千万别把index.html放进 SW 的预缓存列表后就不管了。我见过最坑的一次配置是 SW 缓存了入口 HTML然后index.html一年不更新用户永远停在老版本上。正确做法是把 HTML 设为NetworkFirst策略JS/CSS 走CacheFirst因为带 hash命中了就是对的。用 Workbox 的话一行cleanupOutdatedCaches()能省掉很多手动清理的代码。5. 框架层落地Vue、React、微前端怎么写全局监听是兜底框架层能做的事情更多因为你知道这个 chunk 是哪个路由要用的。5.1 Vue Router 懒加载的重试封装Vue 里最常见的就是component: () import(./views/Home.vue)。把它包一层function lazyLoad(importFn, retries 2) { return () new Promise((resolve, reject) { const attempt () { importFn() .then(resolve) .catch((err) { if (retries-- 0) { setTimeout(attempt, 600); } else { reject(err); } }); }; attempt(); }); } const routes [ { path: /dashboard, component: lazyLoad(() import(./views/Dashboard.vue)), }, ];关键点是重试次数别给多两次足够。两次之后还失败基本可以断定不是网络问题交给全局兜底去刷新页面。另外重试间隔别用固定值业务上我一般用 600ms 和 1200ms 的指数退避避免服务端刚好在抖动时被连续打。还有一个细节值得说在router.onError里补一道钩子因为路由懒加载失败有时不会走到全局unhandledrejection而是被 router 内部消化掉router.onError((err) { if (/Loading chunk|ChunkLoadError/.test(err.message)) { handleChunkError(err); } });5.2 React.lazy 加 ErrorBoundary 的组合React 这边的思路一样但要注意React.lazy的失败会往上冒泡到最近的 Suspense 边界需要 ErrorBoundary 接住。const lazyRetry (importFn, name) React.lazy(() { const key retry-${name}; return new Promise((resolve, reject) { const attempt (n) { importFn() .then(resolve) .catch((err) { if (n 0) { setTimeout(() attempt(n - 1), 600); } else { reject(err); } }); }; attempt(2); }); });ErrorBoundary 里除了展示降级 UI还要做一件事判断错误是不是 chunk 类是的话触发恢复流程。我一般会在componentDidCatch里调用同一个handleChunkError保证全局策略统一。这样不管错误是从哪里冒出来的最终的恢复行为都是一致的维护成本低。顺带说一句如果你的页面有比较重的首屏依赖可以考虑把关键路由的 chunk 改成preload而不是懒加载。代价是首屏体积变大收益是少一次网络往返。这个取舍要看业务B 端系统我更倾向保守一点直接把主路径的 chunk 打进主包。5.3 微前端与内嵌场景的特殊处理微前端是 chunk 问题的重灾区因为主应用和子应用各有各的构建产物版本还可能独立发布。最常见的故障是主应用发了新版子应用没发主应用加载子应用入口时拿到了旧的资源清单一堆 404。我的处理原则是三条。第一子应用入口 HTML 必须加时间戳参数避免被浏览器或 CDN 缓存形如/child/index.html?v20241120143000。第二加载子应用失败要有降级页面而不是让主应用的整个路由崩掉qiankun 的loadMicroApp支持传timeout配合 catch 就能做到。第三主应用和子应用的资源目录要按各自的版本号隔离不要混在一个/static/下否则清理旧版本的时候很容易误删。至于 iframe 内嵌的场景问题会更直接iframe 里加载的页面是别人的域名你控制不了缓存策略。这种情况我建议在 URL 上加一个版本参数并且在内嵌页里也部署同一套 chunk 错误监听通过postMessage把错误抛给父页面处理。6. 同名不同病也叫 chunk failed 的后端报错写到这里必须插一节因为最近搜chunk failed的人里面有很大一部分找的根本不是前端问题。6.1 数据侧那条 transmit chunk rpc failed最近热词里有一条starrocks transmit chunk rpc failed还有stream disconnected before completion: failed to process mtmd chunk。这两条跟前面讲的完全是两个世界的东西。StarRocks 里的 chunk 指的是执行引擎在节点之间传输数据块的单位。一条查询在多个 BE 节点上并行执行中间结果需要以 chunk 为单位在节点之间通过 RPC 传递。出现transmit chunk rpc failed通常意味着某次 RPC 传输没完成常见诱因是节点间的网络抖动、目标节点负载过高导致响应超时、查询并发太高把传输队列打满或者单个 chunk 太大造成内存压力。后面那条stream disconnected before completion则是流式传输中途断开多出现在处理多模态数据这类大块内容的场景本质上是数据还没传完通道先关了。它可能是上游主动取消比如查询被 kill、下游异常退出也可能是超时保护触发。这两类的排查方向和前端完全不同要看的是集群节点的网络质量、负载均衡、并发配置、超时参数而不是浏览器缓存和 chunk hash。6.2 三句话判断你遇到的是哪一类经常有人在群里贴一句chunk failed 怎么解决然后被引导到完全不相关的文档上。我给你三句话的判断方法看报错位置。在浏览器控制台里是前端在服务端日志、数据库日志、集群监控里是后端。看有没有文件名。前端报错一定带一个xxx.hash.js之类的资源路径因为error.request就是那个文件后端报错带的是节点 ID、查询 ID、任务 ID。看能不能刷新恢复。前端 chunk 失败刷新一下大概率好了后端数据传输失败刷新页面是没有任何用的得去查集群。把这三条记住基本就不会走错路。7. 排查路径与我踩过的坑7.1 从用户反馈到复现的六步走我把这几年处理这类问题的流程固化成了六步团队里的新人照着走基本不会跑偏拿到原始报错。一定要原始的不要用户转述的打不开。让用户截图控制台或者直接从监控里捞error.request。比对版本。把error.request里的文件名和当前线上index.html引用的文件名对一遍对不上就是版本错位。确认缓存策略。用curl -I看一下index.html的Cache-Control是不是no-cache或者no-store。看分布。错误是按时间集中发版窗口还是按地域集中CDN 节点还是按用户集中个人网络问题。本地复现。用无痕模式 禁用缓存DevTools 里勾上 Disable cache跑一遍看能不能稳定复现。确认清理策略。检查发布脚本是不是把旧版本目录删了保留份数够不够。这六步走下来绝大多数问题在第二步就能定位。7.2 几个我印象深刻的坑第一个坑是只监听了一个事件。前面提过我早期只监听unhandledrejection结果 CSS chunk 加载失败完全捕获不到监控上的错误量只有真实值的六成导致我们对问题严重性判断偏低拖了两个月才认真处理。第二个坑是刷新逻辑用了localStorage。上线后收到用户投诉说页面每隔一会儿就自己刷一下。查了半天发现是某个用户的环境本身有问题localStorage的计数永不过期他一旦触发过三次之后每次打开页面都直接进降级提示。改成sessionStorage之后问题消失。第三个坑是重试和刷新同时触发。路由层在重试全局监听也在刷新两者打架用户看到页面闪一下又回去了体验极差。后来加了一个全局的恢复锁同一时间只允许一个恢复动作执行另一个直接跳过。这个锁用一个模块级变量加时间戳就能实现代码不到十行但解决了一个非常难复现的问题。第四个坑比较有意思是CDN 的 404 页面被当成了资源。某家 CDN 在资源不存在时会返回一个自定义的错误页状态码是 200内容类型是text/html。结果我们的重试逻辑一直判定请求成功但解析失败反复重试直到超时。后来改成检查response.headers.get(content-type)只要不是 JS 类型就直接判定失败。这个改动之后版本错位的检测速度快了一个数量级。第五个坑是忽略了chunkLoadTimeout的副作用。把超时从 120 秒缩到 30 秒之后有一批在网络条件比较差的地区比如偏远地区的移动网络的用户本来等 40 秒能加载出来的 chunk现在 30 秒就失败了进而在恢复流程里刷新页面可刷新之后网络还是差形成了一次失败循环。后来折中改成 60 秒并给恢复流程加了网络类型判断——如果是effectiveType为2g或slow-2g的用户就不刷新了直接给一个带重试按钮的提示页。最后再分享一个小技巧如果你的项目用了大量按需加载的第三方库可以在构建产物里加一份chunk 依赖清单把哪个路由依赖哪些 chunk导出成一个 JSON 文件随包发布。出问题的时候拿到error.request就能立刻反查出是哪个页面、哪条业务线受影响。这份清单我一般用 webpack 的stats输出配合一个几十行的脚本生成成本很低但对定位问题帮助极大。这套东西加起来大概几百行代码集中在三个文件里一次投入之后我们线上因为 chunk 加载失败导致的用户投诉基本清零了。剩下来的都是网络环境本身的问题那部分我们处理不了也不该由前端来兜。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

全能文件管理工具实战:批量重命名与高效文件处理指南 2026/10/1 4:58:20

全能文件管理工具实战:批量重命名与高效文件处理指南

1. 为什么还需要一款“全能文件管理工具”1.1 系统自带文件管理器到底差在哪先聊一个可能被很多人忽略的事实:现代操作系统自带的文件管理器,其实做得很“够用”,但离“好用”还有很大的距离。Windows Explorer 能复制、粘贴、删除、重命名&a…

阅读更多 →
ComfyUI+PS商业工作流:从AI生成到精修交付的完整实战 2026/10/1 4:58:20

ComfyUI+PS商业工作流:从AI生成到精修交付的完整实战

前阵子接了个茶叶品牌的新品系列视觉项目,品牌方要求一周内产出六款不同口味的包装主视觉。沟通时我就意识到,如果全走 PS 手工合成,找素材、抠图、调光影就得耗掉大半时间;如果完全交给 AI 裸出图,品牌元素统一、文字…

阅读更多 →
Twitter情感分析实战:10MB数据集与20个源码文件的完整工程链路 2026/10/1 4:58:20

Twitter情感分析实战:10MB数据集与20个源码文件的完整工程链路

简介:这份资源面向希望上手NLP情感分析实战的机器学习学习者与数据科学从业者,围绕Twitter推文情感分类任务,提供从数据清洗、特征工程到多模型对比的完整代码实现。包内共23个文件,以20个Python源代码为主,另含2个CSV…

阅读更多 →
Python+Selenium+Spark:淘宝商品爬虫与数据可视化系统实战 2026/10/1 4:58:20

Python+Selenium+Spark:淘宝商品爬虫与数据可视化系统实战

淘宝商品爬虫,看着简单,但要是把 Python、Flask、Selenium、Spark、Hadoop 和 ECharts 串成一个完整的毕设项目,那工作量就不是抓几个商品标题那么简单了。当时我拿这个题目做毕业设计,前前后后折腾了两个月,踩过的坑比…

阅读更多 →
Python+Flask淘宝商品爬虫系统:从Selenium采集到Spark分析的大数据实战 2026/10/1 4:58:19

Python+Flask淘宝商品爬虫系统:从Selenium采集到Spark分析的大数据实战

如果你正在准备大数据方向的毕业设计,或者想快速把爬虫、数据分析、可视化串成一条完整的技术链路,那这套基于PythonFlask的淘宝商品爬虫系统值得你花时间拆一拆。我当初做这个项目,最直接的感受是:它不是一个简单的爬虫Demo&…

阅读更多 →
微医互联网医院平台对接实战:接口调用、电子处方与监管上报全解析 2026/10/1 4:58:13

微医互联网医院平台对接实战:接口调用、电子处方与监管上报全解析

简介:这份PPT资料系统梳理了微医互联网医院平台的产品设计,面向互联网医疗产品经理、医疗信息化从业者及医院管理者,帮助理解在线复诊、远程会诊等业务的完整功能架构。资源为1个pptx文件,压缩包约25MB,以图文并茂的幻…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉