@rsbuild/plugin-assets-retry 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,380 @@
1
+ # @rsbuild/plugin-assets-retry
2
+
3
+ 用于在静态资源加载失败时自动发起重试请求。
4
+
5
+ <p>
6
+ <a href="https://npmjs.com/package/@rsbuild/plugin-assets-retry">
7
+ <img src="https://img.shields.io/npm/v/@rsbuild/plugin-assets-retry?style=flat-square&colorA=564341&colorB=EDED91" alt="npm version" />
8
+ </a>
9
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="license" />
10
+ <a href="https://npmcharts.com/compare/@rsbuild/plugin-assets-retry?minimal=true"><img src="https://img.shields.io/npm/dm/@rsbuild/plugin-assets-retry.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="downloads" /></a>
11
+ </p>
12
+
13
+ ## 快速开始
14
+
15
+ ### 安装插件
16
+
17
+ 你可以通过如下的命令安装插件:
18
+
19
+ ```bash
20
+ # npm
21
+ npm add @rsbuild/plugin-assets-retry -D
22
+
23
+ # yarn
24
+ yarn add @rsbuild/plugin-assets-retry -D
25
+
26
+ # pnpm
27
+ pnpm add @rsbuild/plugin-assets-retry -D
28
+
29
+ # bun
30
+ bun add @rsbuild/plugin-assets-retry -D
31
+ ```
32
+
33
+ ### 注册插件
34
+
35
+ 你可以在 `rsbuild.config.ts` 文件中注册插件:
36
+
37
+ ```ts
38
+ import { pluginAssetsRetry } from "@rsbuild/plugin-assets-retry";
39
+
40
+ export default {
41
+ plugins: [pluginAssetsRetry()],
42
+ };
43
+ ```
44
+
45
+ ## 选项
46
+
47
+ 你可以通过选项来配置资源加载失败时的重试逻辑。
48
+
49
+ - **类型:**
50
+
51
+ ```ts
52
+ type AssetsRetryHookContext = {
53
+ times: number;
54
+ domain: string;
55
+ url: string;
56
+ tagName: string;
57
+ isAsyncChunk: boolean;
58
+ };
59
+
60
+ type AssetsRetryOptions = {
61
+ type?: string[];
62
+ domain?: string[];
63
+ max?: number;
64
+ test?: string | ((url: string) => boolean);
65
+ crossOrigin?: boolean | "anonymous" | "use-credentials";
66
+ inlineScript?: boolean;
67
+ delay?: number | ((context: AssetsRetryHookContext) => number);
68
+ onRetry?: (context: AssetsRetryHookContext) => void;
69
+ onSuccess?: (context: AssetsRetryHookContext) => void;
70
+ onFail?: (context: AssetsRetryHookContext) => void;
71
+ };
72
+ ```
73
+
74
+ - **默认值:**
75
+
76
+ ```ts
77
+ const defaultOptions = {
78
+ type: ["script", "link", "img"],
79
+ domain: [],
80
+ max: 3,
81
+ test: "",
82
+ crossOrigin: false,
83
+ delay: 0,
84
+ onRetry: () => {},
85
+ onSuccess: () => {},
86
+ onFail: () => {},
87
+ };
88
+ ```
89
+
90
+ ### domain
91
+
92
+ - **类型:** `string[]`
93
+ - **默认值:** `[]`
94
+
95
+ 指定资源加载失败时的重试域名列表。在 `domain` 数组中,第一项是当前使用的域名,后面几项为备用域名。当某个域名的资源请求失败时,Rsbuild 会在数组中找到该域名,并替换为数组的下一个域名。
96
+
97
+ 比如:
98
+
99
+ ```js
100
+ pluginAssetsRetry({
101
+ domain: ["https://cdn1.com", "https://cdn2.com", "https://cdn3.com"],
102
+ });
103
+ ```
104
+
105
+ 添加以上配置后,当 `cdn1.com` 域名的资源加载失败时,请求域名会自动降级到 `cdn2.com`。
106
+
107
+ 如果 `cdn2.com` 的资源也请求失败,则会继续请求 `cdn3.com`。
108
+
109
+ ### type
110
+
111
+ - **类型:** `string[]`
112
+ - **默认值:** `['script', 'link', 'img']`
113
+
114
+ 用于指定需要进行重试的 HTML 标签类型。默认会处理 script 标签、link 标签和 img 标签,对应 JS 代码、CSS 代码和图片。
115
+
116
+ 比如只对 script 标签和 link 标签进行处理:
117
+
118
+ ```js
119
+ pluginAssetsRetry({
120
+ type: ["script", "link"],
121
+ });
122
+ ```
123
+
124
+ ### max
125
+
126
+ - **类型:** `number`
127
+ - **默认值:** `3`
128
+
129
+ 单个资源的最大重试次数。比如:
130
+
131
+ ```js
132
+ pluginAssetsRetry({
133
+ max: 5,
134
+ });
135
+ ```
136
+
137
+ ### test
138
+
139
+ - **类型:** `string | ((url: string) => boolean) | undefined`
140
+ - **默认值:** `undefined`
141
+
142
+ 匹配资源 URL 的正则表达式或函数,默认匹配所有资源。比如:
143
+
144
+ ```js
145
+ pluginAssetsRetry({
146
+ test: /cdn\.example\.com/,
147
+ });
148
+ ```
149
+
150
+ ### crossOrigin
151
+
152
+ - **类型:** `undefined | boolean | 'anonymous' | 'use-credentials'`
153
+ - **默认值:** `与 html.crossorigin 一致`
154
+
155
+ 在发起资源重新请求时,Rsbuild 会重新创建 `<script>` 标签,此选项可以设置这些标签的 `crossorigin` 属性。
156
+
157
+ 默认情况下,`crossOrigin` 的值会与 `html.crossorigin` 配置项保持一致,无须额外配置。如果你需要对重新创建的标签进行单独配置,可以使用该选项,比如:
158
+
159
+ ```js
160
+ pluginAssetsRetry({
161
+ crossOrigin: true,
162
+ });
163
+ ```
164
+
165
+ ### onRetry
166
+
167
+ - **类型:** `undefined | (context: AssetsRetryHookContext) => void`
168
+
169
+ 资源重试时的回调函数。比如:
170
+
171
+ ```js
172
+ pluginAssetsRetry({
173
+ onRetry: ({ times, domain, url, tagName, isAsyncChunk }) => {
174
+ console.log(
175
+ `Retry ${times} times, domain: ${domain}, url: ${url}, tagName: ${tagName}, isAsyncChunk: ${isAsyncChunk}`
176
+ );
177
+ },
178
+ });
179
+ ```
180
+
181
+ ### onSuccess
182
+
183
+ - **类型:** `undefined | (context: AssetsRetryHookContext) => void`
184
+
185
+ 资源重试成功时的回调函数。比如:
186
+
187
+ ```js
188
+ pluginAssetsRetry({
189
+ onSuccess: ({ times, domain, url, tagName, isAsyncChunk }) => {
190
+ console.log(
191
+ `Retry ${times} times, domain: ${domain}, url: ${url}, tagName: ${tagName}, isAsyncChunk: ${isAsyncChunk}`
192
+ );
193
+ },
194
+ });
195
+ ```
196
+
197
+ ### onFail
198
+
199
+ - **类型:** `undefined | (context: AssetsRetryHookContext) => void`
200
+
201
+ 资源重试超过最大重试次数时的回调函数。比如:
202
+
203
+ ```js
204
+ pluginAssetsRetry({
205
+ onFail: ({ times, domain, url, tagName, isAsyncChunk }) => {
206
+ console.log(
207
+ `Retry ${times} times, domain: ${domain}, url: ${url}, tagName: ${tagName}, isAsyncChunk: ${isAsyncChunk}`
208
+ );
209
+ },
210
+ });
211
+ ```
212
+
213
+ ### addQuery
214
+
215
+ - **类型:**
216
+
217
+ ```ts
218
+ type AddQuery =
219
+ | boolean
220
+ | ((context: { times: number; originalQuery: string }) => string);
221
+ ```
222
+
223
+ - **默认值:** `false`
224
+
225
+ 是否在资源重试时添加 query,这样可以避免被浏览器、CDN 缓存影响到重试的结果。
226
+
227
+ 当设置为 `true` 时,请求时会在 query 中添加 `retry=${times}`,按照 `retry=1`,`retry=2`,`retry=3` 依次请求,比如:
228
+
229
+ 1. 假设请求的静态资源为 `https://js.cdn.net/foo.js`,请求失败后会自动重试 `https://js.cdn.net/foo.js?retry=${times}`
230
+
231
+ 2. 假设请求的静态资源为 `https://js.cdn.net/foo.js?version=1`,请求失败后会自动重试 `https://js.cdn.net/foo.js?version=1&retry=${times}`
232
+
233
+ 当你想要自定义 query 时,可以传入一个函数,比如:
234
+
235
+ - **示例:** 请求的所有资源都不含 query:
236
+
237
+ ```js
238
+ pluginAssetsRetry({
239
+ addQuery: ({ times }) => {
240
+ return times === 3
241
+ ? `?retryCount=${times}&isLast=1`
242
+ : `?retryCount=${times}`;
243
+ },
244
+ });
245
+ ```
246
+
247
+ - **示例:** 当请求的某些资源中含有 query 时,可以使用 `originalQuery` 读取:
248
+
249
+ ```js
250
+ pluginAssetsRetry({
251
+ addQuery: ({ times, originalQuery }) => {
252
+ const query =
253
+ times === 3 ? `retryCount=${times}&isLast=1` : `retryCount=${times}`;
254
+ return originalQuery ? `${originalQuery}&${query}` : `?${query}`;
255
+ },
256
+ });
257
+ ```
258
+
259
+ ### inlineScript
260
+
261
+ - **类型:** `boolean`
262
+ - **默认值:** `true`
263
+
264
+ 是否将 Assets Retry 插件的运行时 JavaScript 代码内联到 HTML 文件中。
265
+
266
+ 如果你不希望在 HTML 文件中插入相关代码,可以将 `inlineScript` 设置为 `false`:
267
+
268
+ ```js
269
+ pluginAssetsRetry({
270
+ inlineScript: false,
271
+ });
272
+ ```
273
+
274
+ 添加以上配置后,Assets Retry 插件的运行时代码会被抽取为一个独立的 `assets-retry.[version].js` 文件,并输出到产物目录下。
275
+
276
+ 这种方式的弊端在于,`assets-retry.[version].js` 自身有加载失败的可能性。如果出现这种情况,静态资源重试的逻辑就无法生效。因此,我们更推荐将运行时代码内联到 HTML 文件中。
277
+
278
+ ### minify
279
+
280
+ - **类型:** `boolean`
281
+ - **默认值:** `process.env.NODE_ENV === 'production'`
282
+
283
+ 是否对运行时 JavaScript 代码开启代码压缩。
284
+
285
+ 默认情况下,会受到 [output.minify](/config/output/minify) 配置的影响。
286
+
287
+ ```js
288
+ pluginAssetsRetry({
289
+ minify: true,
290
+ });
291
+ ```
292
+
293
+ ### delay
294
+
295
+ - **类型:** `number | ((context: AssetsRetryHookContext) => number)`
296
+ - **默认值:** `0`
297
+
298
+ 在资源重试前的延迟时间,单位为毫秒。
299
+
300
+ 你可以传入一个数字:
301
+
302
+ ```js
303
+ // 延时 1s 重试
304
+ pluginAssetsRetry({
305
+ delay: 1000,
306
+ });
307
+ ```
308
+
309
+ 也可以传入一个函数,函数的参数为 `AssetsRetryHookContext`,返回值为延迟时间:
310
+
311
+ ```js
312
+ // 通过次数来计算延迟时间
313
+ pluginAssetsRetry({
314
+ delay: (ctx) => (ctx.times + 1) * 1000,
315
+ });
316
+ ```
317
+
318
+ ## 注意事项
319
+
320
+ 当你使用 Assets Retry 插件时,Rsbuild 会分别向 HTML 和 [Rspack Runtime](https://rspack.dev/zh/misc/glossary#runtime) 中注入运行时代码,并将 Assets Retry 插件配置的内容序列化后插入到这些代码中,因此你需要注意:
321
+
322
+ - 避免在 Assets Retry 插件中配置敏感信息,比如内部使用的 token。
323
+ - 避免在 `onRetry`,`onSuccess`,`onFail` 中引用函数外部的变量或方法。
324
+ - 避免在 `onRetry`,`onSuccess`,`onFail` 中使用有兼容性问题的语法,因为这些函数会被直接内联到 HTML 中。
325
+
326
+ 以下是一个错误示例:
327
+
328
+ ```js
329
+ import { someMethod } from "utils";
330
+
331
+ pluginAssetsRetry({
332
+ onRetry() {
333
+ // 错误用法,包含了敏感信息
334
+ const privateToken = "a-private-token";
335
+
336
+ // 错误用法,使用了外部的方法
337
+ someMethod(privateToken);
338
+ },
339
+ });
340
+ ```
341
+
342
+ ## 使用限制
343
+
344
+ 以下场景 Assets Retry 插件可能无法生效:
345
+
346
+ ### 模块联邦
347
+
348
+ 对于模块联邦加载的远程模块,你可以使用模块联邦 2.0 的 [@module-federation/retry-plugin](https://www.npmjs.com/package/@module-federation/retry-plugin) 来实现静态资源重试。
349
+
350
+ ### 微前端
351
+
352
+ 如果你的工程是微前端应用(比如 Garfish 子应用),那么 Assets Retry 插件可能无法生效,因为微前端子应用通常不是基于 `<script>` 标签直接加载的。
353
+
354
+ 如果你需要对微前端场景的资源加载进行重试,请联系微前端框架的开发者,以寻找相应的解决方案。
355
+
356
+ ### 自定义模版中的资源
357
+
358
+ Assets Retry 插件通过监听页面 error 事件来获悉当前资源是否加载失败需要重试。因此,如果自定义模版中的资源执行早于 Assets Retry 插件,那 Assets Retry 插件无法监听到该资源加载失败的事件,retry 无法对其生效。
359
+
360
+ 如果想要 Assets Retry 插件对自定义模版中的资源生效,可参考 [自定义插入示例](https://github.com/rspack-contrib/html-rspack-plugin/tree/main/examples/custom-insertion-position) 来修改 [html.inject](/config/html/inject) 配置和自定义模版。
361
+
362
+ ```diff
363
+ <!DOCTYPE html>
364
+ <html>
365
+ <head>
366
+ <meta charset="utf-8">
367
+ <title>custom template</title>
368
+ + <%= htmlPlugin.tags.headTags %>
369
+ <script src="https://example.com/assets/a.js"></script>
370
+ </head>
371
+ <body>
372
+ <div id="root" />
373
+ + <%= htmlPlugin.tags.bodyTags %>
374
+ </body>
375
+ </html>
376
+ ```
377
+
378
+ ## License
379
+
380
+ [MIT](./LICENSE).