浏览器对<script src>加载失败无内置重试机制,仅触发onerror事件且不抛异常;容错需开发者手动实现防重复降级、新建script插入、DOM就绪检查及fallback不重试。

浏览器对 <script src> 加载失败本身**没有内置重试机制**,网络断开时会直接触发加载错误(如 0 状态码、net::ERR_INTERNET_DISCONNECTED),不会自动重试或切换备用源。容错能力完全依赖开发者主动实现。
浏览器原生行为:只报错,不重试
当网络中断导致 <script src="a.js"> 请求失败时:
- 控制台显示类似
Failed to load resource: net::ERR_INTERNET_DISCONNECTED的警告,但不抛出 JS 异常,后续脚本仍会继续执行 -
onerror事件可捕获该失败(需写在标签上或通过addEventListener('error', ..., true)全局监听) - 浏览器绝不会自动重发请求,也不会尝试其他 URL 或降级路径
- 若使用
async或defer,失败不影响 HTML 解析,但脚本功能彻底缺失
可靠容错必须手动实现的四个关键点
仅靠 onerror="this.src='fallback.js'" 这类简单写法极易引发问题(如 Safari 不触发、无限循环、200 错误页无法捕获)。生产环境需满足:
-
防重复降级:用
data-id或全局对象(如window.__triedFallbacks)标记已尝试的资源,避免多次 fallback -
新建 script 元素插入:fallback 必须调用
document.createElement('script')创建新节点,不能复用原<script>标签 -
检查 DOM 就绪状态:插入前判断
document.readyState,若为'loading',应延迟到DOMContentLoaded后再 append,否则可能因 DOM 未就绪导致执行报错 - fallback 自身不重试:二级脚本(如本地备份)也失败时,只记录日志,不再尝试三级 fallback —— 避免链式崩溃
实用 fallback 示例(含网络恢复后自动重试逻辑)
以下代码可在网络短暂中断后自动尝试备用 CDN,并支持有限重试:
<!-- 放在所有 script 标签之前 -->
<script><br> const fallbackUrls = [<br> "https://cdn1.example.com/lib.js",<br> "https://cdn2.example.com/lib.js",<br> "/local/lib.js"<br> ];<br> const maxRetries = 2;<br> const retryState = {};<br><br> window.addEventListener('error', (e) => {<br> if (e.target.tagName === 'SCRIPT' && !(e instanceof ErrorEvent)) {<br> const url = new URL(e.target.src);<br> const key = url.pathname;<br> if (!retryState[key]) retryState[key] = { count: 0, index: 0 };<br> if (retryState[key].count < maxRetries && retryState[key].index < fallbackUrls.length) {<br> retryState[key].count++;<br> const fallback = document.createElement('script');<br> fallback.src = fallbackUrls[retryState[key].index++];<br> fallback.onerror = () => console.warn('Fallback failed:', fallback.src);<br> if (document.readyState === 'loading') {<br> document.addEventListener('DOMContentLoaded', () => document.head.appendChild(fallback));<br> } else {<br> document.head.appendChild(fallback);<br> }<br> }<br> }<br> }, true);<br></script>
补充建议:提升首次加载鲁棒性
比“断网重试”更有效的做法是预防性优化:
- 关键脚本用
defer+ 备用nomodule回退,兼顾现代与旧浏览器 - 引入 SRI(
integrity属性)校验资源完整性,防止 CDN 被劫持返回恶意内容 - 对非核心脚本(如统计、埋点)使用动态加载 +
fetch()检测网络状态后再决定是否插入,避免白屏或阻塞 - 服务端配合:Nginx 或 CDN 配置 503/离线页面,前端监听
navigator.onLine做轻量提示(注意该 API 有局限性,仅作辅助)


















