
脚本加载超时和网络重试不是“加个 timeout 就行”的小问题,而是影响首屏可用性、错误率和用户留存的关键链路。核心在于:区分加载阶段(下载 vs 执行)、识别失败类型(网络中断 vs 404 vs CORS)、控制重试节奏与降级边界。
script 标签的超时机制必须手动封装
原生 <script></script> 标签不支持超时属性。即使使用 async 或 defer,下载卡住仍会无限等待。可靠做法是用 Promise 包装动态创建过程:
- 创建
script元素,设置src,挂载到head或body - 同时启动计时器(如 8–10 秒),超时后调用
reject并移除该 script 节点 - 监听
load和error事件,成功则resolve,失败则reject - 注意 IE 需兼容
onreadystatechange,状态为"loaded"或"complete"时触发
模块脚本(type="module")需绕过浏览器默认静默失败
ES 模块在加载失败时不会抛出可捕获的全局 error,也不会触发 window.onerror。必须依赖构建层或运行时拦截:
- 构建时用插件(如
rollup-plugin-visualizer)检查模块图,提前暴露循环/缺失依赖 - 运行时通过
import()动态导入 +try/catch捕获加载异常,再结合重试逻辑 - 避免在顶层
import中引入高风险 CDN 资源;关键模块优先走本地或预加载(<link rel="modulepreload">)
重试策略要分层设计,而非简单轮换域名
盲目重试可能放大弱网压力,甚至引发雪崩。推荐三级响应:
-
一级快速切换:首次失败后,500ms 内用备用 CDN 域名重试(如从
cdn-a.example.com切到cdn-b.example.com) -
二级降级加载:重试失败后,改用更小体积的兼容版本(如
lodash-core.min.js替代完整版)或本地兜底资源 - 三级功能熔断:连续失败 2 次且非核心模块,直接跳过加载,记录日志并展示轻量提示(如“部分功能暂不可用”)
监控必须精准过滤,避免误报干扰
全局 error 事件监听会混入 JS 运行时错误、资源跨域、内联脚本语法错误等噪声。只捕获真实加载失败需满足三个条件:
- 事件目标
e.target.tagName === 'SCRIPT' - 事件对象不是
ErrorEvent实例(即!(e instanceof ErrorEvent)) - 检查
e.target.src是否为有效 URL,排除空 src 或 data: 协议
满足以上才视为真实网络加载失败,此时再触发重试逻辑或上报埋点。











