English | 简体中文
当页面中的脚本、样式、图片资源无法正常加载时,自动重试加载失败的资源。支持备用域名、动态导入(dynamic import),无需改动现有代码,仅需 3 KB (gzipped)。

$ npm install assets-retry --save
然后通过 webpack 配置 内联到页面的 head 标签中,并置于所有资源开始加载之前。
script 标签引用如果你懒得折腾 webpack 配置,可以将 assets-retry.umd.js 直接内联到 <head> 标签中,并置于所有资源开始加载之前。
使用起来非常简单,只需要初始化并传入域名列表即可:
// assetsRetryStatistics 中包含所有资源重试的相关信息
var assetsRetryStatistics = window.assetsRetry({
// 域名列表,只有在域名列表中的资源,才会被重试
// 使用以下配置,当 https://your.first.domain/js/1.js 加载失败时
// 会自动使用 https://your.second.domain/namespace/js/1.js 重试
domain: ['your.first.domain', 'your.second.domain/namespace'],
// 可选,最大重试次数,默认 3 次
maxRetryCount: 3,
// 可选,通过该参数可自定义 URL 的转换方式
onRetry: function(currentUrl, originalUrl, statistics) {
return currentUrl
},
// 对于给定资源,要么调用 onSuccess ,要么调用 onFail,标识其最终的加载状态
// 加载详细信息(成功的 URL、失败的 URL 列表、重试次数)
// 可以通过访问 assetsRetryStatistics[currentUrl] 来获取
onSuccess: function(currentUrl) {
console.log(currentUrl, assetsRetryStatistics[currentUrl])
},
onFail: function(currentUrl) {
console.log(currentUrl, assetsRetryStatistics[currentUrl])
}
})
当使用以上代码初始化完毕后,以下内容便获得了加载失败重试的能力:
html 中使用 <script> 标签引用的脚本html 中使用 <link> 标签引用的样式 (跨域 CSS 需要正确配置)html 中使用 <img> 标签引用的图片document.createElement('script') 加载的脚本(如 webpack 的动态导入)css 中(包含同步与异步)使用的 background-image 图片assetsRetry 接受一个配置对象 AssetsRetryOptions ,其类型签名为:
interface AssetsRetryOptions {
maxRetryCount: number
onRetry: RetryFunction
onSuccess: SuccessFunction
onFail: FailFunction
domain: Domain
}
type RetryFunction = (
currentUrl: string,
originalUrl: string,
retryCollector: null | RetryStatistics
) => string | null
interface RetryStatistics {
retryTimes: number
succeeded: string[]
failed: string[]
}
type SuccessFunction = (currentUrl: string) => void
type FailFunction = (currentUrl: string) => void
type Domain = string[] | { [x: string]: string }
具体说明如下:
domain: 域名列表,可配置为数组或对象类型{ 'a.cdn': 'b.cdn', 'c.cdn': 'd.cdn' } 表示在 a.cdn 失败的资源应从 b.cdn 重试,在 c.cdn 失败的资源应从 d.cdn 重试。maxRetryCount: 每个资源的最大重试次数onRetry: 在每次尝试重新加载资源时执行currentUrl: 即将被选为重试地址的 URLoriginalUrl: 上一次加载失败的 URLretryCollector: 为当前资源的数据收集对象,如果资源为 CSS 中使用 url 引用的图片资源,该参数为 null 。当该参数不为 null 时,包含 3 个属性:retryTimes: 表示当前为第 x 次重试(从 1 开始)failed: 已失败的资源列表(从同一域名加载多次时,可能重复)succeeded: 已成功的资源列表null 对象。null 时,表示终止该次重试onSuccess: 在域名列表内的资源最终加载成功时执行:currentUrl: 资源名,可通过该名称来找到当前资源的数据收集对象onFail: 在域名列表内的资源最终加载失败时执行:currentUrl: 资源名,可通过该名称来找到当前资源的数据收集对象Assets-Retry 的实现主要分为三部分:
<script>, <link>, <img>)并重试这部分实现较为简单,监听 document 对象的 error 事件便能够捕获到静态资源加载失败的错误。当 event.target 为需要重试的元素时,重试加载该元素即可。但需要注意以下场景:
<script src="https://github.com/Nikaple/assets-retry/raw/v0.3.4/vendor.js"></script>
<script src="https://github.com/Nikaple/assets-retry/raw/v0.3.4/app.js"></script>
在上面的代码中, app.js 依赖 vendor.js 中的功能,这在使用 webpack 打包的项目中极其常见,如果使用 document.createElement('script') 来对其进行重试,在网络环境不确定的情况下,app.js 很有可能比 vendor.js 先加载完毕,导致页面报错不可用。
所以对于在 html 中同步加载的 script 标签,在页面还未加载完毕时,需要使用 document.write,阻塞式地将 script 标签动态添加到 html 中。
以 webpack 的动态加载脚本为例(仅保留关键代码):
function requireEnsure(chunkId) {
// 加载 chunk 的 promise
var promise = new Promise(function(resolve, reject) {
installedChunkData = installedChunks[chunkId] = [resolve, reject]
})
installedChunkData[2] = promise
// start chunk loading
var script = document.createElement('script')
var onScriptComplete
script.charset = 'utf-8'
script.timeout = 120
script.src = jsonpScriptSrc(chunkId)
onScriptComplete = function(event) {
var chunk = installedChunks[chunkId]
if (chunk) {
// chunk[1] 加载 script 的 reject 回调
chunk[1](new Error(/* ... */))
}
}
script.onerror = script.onload = onScriptComplete
document.head.appendChild(script)
}
在 webpack 等模块加载器中使用动态导入时, webpack 便会用上面的 requireEnsure 方法来保证对应的动态 chunk 被加载。如果某个 chunk 加载失败,则会进入 installChunkData[2] 中储存 Promise 的 reject 流程,而 Promise 一旦进入 rejected 状态,就再也无法改变到其他状态了。也就是说, webpack 并不会给我们重试的机会。
如何打破这种局面?摆在我们面前的只有两条路:
为了降低集成成本,我们选择了第二种方案,即在运行时动态改写 document.createElement, Node#appendChild 等方法。在集成 Assets-Retry 后,调用 document.createElement API 并不会创建一个真正的 HTMLScriptElement ,取而代之的是一个 HookedScript 对象。并且,在 Node#appendChild 等方法中,如果检测到当前元素为 HookedScript 对象,则将appendChild 目标转换为其内部保存的真正的 HTMLScriptElement 。
增加这层代理后,我们就可以对 script 标签上的 onload, onerror 回调进行拦截,并进行重试处理。如果用户想设置对象的其他属性,如 src, type,则会设置到真正的 script 标签上,保证其他功能不受影响。
该部分通过 CSSStyleSheet 动态改变页面样式实现,当遇到图片类属性(如 background-image, border-image, list-style-image)时,自动添加一条包含备用域名的规则到网页样式中,浏览器便会自动发起重试,直到任一请求成功或均以失败告终。
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
|
|---|---|---|---|---|---|---|---|
| 47+ ✔ | 15+ ✔ | 32+ ✔ | 10+ ✔ | 34+ ✔ | 10+ ✔ | 10+ ✔ | 4.4+ ✔ |
crossorigin="anonymous" 属性。npm t: Run test suitenpm start: Run npm run build in watch modenpm run test:watch: Run test suite in interactive watch modenpm run test:prod: Run linting and generate coveragenpm run build: Generate bundles and typings, create docsnpm run lint: Lints codenpm run commit: Commit using conventional commit style (husky will tell you to use it if you haven't :wink:)感谢 realworld.io 提供的 Demo App。
$ claude mcp add assets-retry \
-- python -m otcore.mcp_server <graph>