resource-preloader 2.0.4 → 2.0.5
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.
- package/dist/resource-loader.amd.js +4 -4
- package/dist/resource-loader.amd.js.map +1 -1
- package/dist/resource-loader.amd.min.js.map +1 -1
- package/dist/resource-loader.cjs.js +4 -4
- package/dist/resource-loader.cjs.js.map +1 -1
- package/dist/resource-loader.cjs.min.js.map +1 -1
- package/dist/resource-loader.esm.js +2 -2
- package/dist/resource-loader.esm.js.map +1 -1
- package/dist/resource-loader.esm.min.js +1 -1
- package/dist/resource-loader.esm.min.js.map +1 -1
- package/dist/resource-loader.umd.js +4 -4
- package/dist/resource-loader.umd.js.map +1 -1
- package/dist/resource-loader.umd.min.js.map +1 -1
- package/dist/resource-preloader.amd.js +386 -362
- package/dist/resource-preloader.amd.js.map +1 -1
- package/dist/resource-preloader.amd.min.js +1 -1
- package/dist/resource-preloader.amd.min.js.map +1 -1
- package/dist/resource-preloader.cjs.js +292 -268
- package/dist/resource-preloader.cjs.js.map +1 -1
- package/dist/resource-preloader.cjs.min.js +1 -1
- package/dist/resource-preloader.cjs.min.js.map +1 -1
- package/dist/resource-preloader.esm.js +292 -268
- package/dist/resource-preloader.esm.js.map +1 -1
- package/dist/resource-preloader.esm.min.js +1 -1
- package/dist/resource-preloader.esm.min.js.map +1 -1
- package/dist/resource-preloader.umd.js +389 -365
- package/dist/resource-preloader.umd.js.map +1 -1
- package/dist/resource-preloader.umd.min.js +1 -1
- package/dist/resource-preloader.umd.min.js.map +1 -1
- package/package.json +4 -3
- package/src/checker.js +53 -0
- package/src/config.js +10 -12
- package/src/index.js +5 -5
- package/src/loader.js +128 -128
- package/src/other.js +4 -4
- package/src/scheduler.js +123 -149
- package/types/index.d.ts +20 -8
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 默认公共配置
|
|
2
2
|
const defaultConfig = {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
timeout: 1500, // 默认超时时间1s
|
|
4
|
+
retry: 0, // 默认不重试
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
/** @var {GlobalConfig} */
|
|
@@ -13,11 +13,11 @@ let globalConfig = { ...defaultConfig };
|
|
|
13
13
|
* @returns {GlobalConfig} 合并后的最终全局配置
|
|
14
14
|
*/
|
|
15
15
|
function setGlobalConfig(config) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
if (typeof config !== "object" || config === null) {
|
|
17
|
+
throw new Error("配置参数必须是一个非空对象");
|
|
18
|
+
}
|
|
19
|
+
globalConfig = { ...globalConfig, ...config };
|
|
20
|
+
return { ...globalConfig };
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -25,7 +25,7 @@ function setGlobalConfig(config) {
|
|
|
25
25
|
* @returns {GlobalConfig} 全局配置
|
|
26
26
|
*/
|
|
27
27
|
function getGlobalConfig() {
|
|
28
|
-
|
|
28
|
+
return { ...globalConfig };
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// 加载器映射表(存储内置+自定义加载器)
|
|
@@ -39,137 +39,137 @@ const loaderMap = new Map();
|
|
|
39
39
|
* @returns {Promise<LoadResult>} 加载结果Promise
|
|
40
40
|
*/
|
|
41
41
|
function wrapLoadPromise(url, type, timeout) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
42
|
+
return new Promise(function (resolve, reject) {
|
|
43
|
+
// 获取加载器处理函数
|
|
44
|
+
const handler = getLoader(type);
|
|
45
|
+
// 超时处理
|
|
46
|
+
const timeoutTimer = setTimeout(() => {
|
|
47
|
+
reject(new Error(`Resource ${url} load timeout (${timeout}ms)`));
|
|
48
|
+
}, timeout);
|
|
49
|
+
|
|
50
|
+
// 执行具体加载逻辑
|
|
51
|
+
handler(url)
|
|
52
|
+
.then(function (result) {
|
|
53
|
+
clearTimeout(timeoutTimer);
|
|
54
|
+
resolve({
|
|
55
|
+
url,
|
|
56
|
+
success: true,
|
|
57
|
+
data: result,
|
|
58
|
+
error: null,
|
|
59
|
+
});
|
|
60
|
+
})
|
|
61
|
+
.catch(function (error) {
|
|
62
|
+
clearTimeout(timeoutTimer);
|
|
63
|
+
reject({
|
|
64
|
+
url,
|
|
65
|
+
success: false,
|
|
66
|
+
data: null,
|
|
67
|
+
error,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/** 初始化内置加载器 **/
|
|
74
74
|
// --------------- 内置JS加载器 ---------------
|
|
75
75
|
loaderMap.set(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
76
|
+
"js",
|
|
77
|
+
/**
|
|
78
|
+
* JS加载器
|
|
79
|
+
* @param {string} url
|
|
80
|
+
* @returns {Promise<HTMLScriptElement|void>}
|
|
81
|
+
*/
|
|
82
|
+
function (url) {
|
|
83
|
+
const script = document.createElement("script");
|
|
84
|
+
return new Promise(function (resolve, reject) {
|
|
85
|
+
script.type = "text/javascript";
|
|
86
|
+
script.src = url;
|
|
87
|
+
script.async = false; // 保持加载顺序(不开启异步)
|
|
88
|
+
script.dataset.flag = "resource-preloader";
|
|
89
|
+
|
|
90
|
+
script.addEventListener("load", function (e) {
|
|
91
|
+
resolve(script);
|
|
92
|
+
});
|
|
93
|
+
script.addEventListener("error", function () {
|
|
94
|
+
reject(new Error(`JS resource ${url} load failed`));
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
document.head.appendChild(script);
|
|
98
|
+
}).finally(function () {
|
|
99
|
+
// 加载完成后移除标签,避免污染DOM
|
|
100
|
+
document.head.removeChild(script);
|
|
101
|
+
});
|
|
102
|
+
},
|
|
103
103
|
);
|
|
104
104
|
// --------------- 内置CSS加载器 ---------------
|
|
105
105
|
loaderMap.set(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
106
|
+
"css",
|
|
107
|
+
/**
|
|
108
|
+
* CSS加载器
|
|
109
|
+
* @param {string} url
|
|
110
|
+
* @returns {Promise<HTMLLinkElement>}
|
|
111
|
+
*/
|
|
112
|
+
function (url) {
|
|
113
|
+
return new Promise(function (resolve, reject) {
|
|
114
|
+
const link = document.createElement("link");
|
|
115
|
+
link.rel = "stylesheet";
|
|
116
|
+
link.href = url;
|
|
117
|
+
link.dataset.flag = "resource-preloader";
|
|
118
|
+
|
|
119
|
+
link.addEventListener("load", function () {
|
|
120
|
+
resolve(link);
|
|
121
|
+
});
|
|
122
|
+
link.addEventListener("error", function () {
|
|
123
|
+
reject(new Error(`CSS resource ${url} load failed`));
|
|
124
|
+
document.head.removeChild(link);
|
|
125
|
+
});
|
|
126
|
+
document.head.appendChild(link);
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
129
|
);
|
|
130
130
|
// --------------- 内置IMG加载器 ---------------
|
|
131
131
|
loaderMap.set(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
132
|
+
"img",
|
|
133
|
+
/**
|
|
134
|
+
* 图片加载器
|
|
135
|
+
* @param url
|
|
136
|
+
* @returns {Promise<HTMLImageElement>}
|
|
137
|
+
*/
|
|
138
|
+
function (url) {
|
|
139
|
+
const img = new Image();
|
|
140
|
+
img.dataset.flag = "resource-preloader";
|
|
141
|
+
return new Promise(function (resolve, reject) {
|
|
142
|
+
img.addEventListener("load", function () {
|
|
143
|
+
resolve(img);
|
|
144
|
+
});
|
|
145
|
+
img.addEventListener("error", function () {
|
|
146
|
+
reject(new Error(`IMG resource ${url} load failed`));
|
|
147
|
+
});
|
|
148
|
+
// img 标签不需要添加到DOM树
|
|
149
|
+
img.src = url;
|
|
150
|
+
});
|
|
151
|
+
},
|
|
152
152
|
);
|
|
153
153
|
// --------------- 内置JSON加载器 ---------------
|
|
154
154
|
loaderMap.set(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
155
|
+
"json",
|
|
156
|
+
/**
|
|
157
|
+
* JSON加载器
|
|
158
|
+
* @param {string} url
|
|
159
|
+
* @returns {Promise<Object|Array|null|undefined>}
|
|
160
|
+
*/
|
|
161
|
+
function (url) {
|
|
162
|
+
return fetch(url)
|
|
163
|
+
.then(function (res) {
|
|
164
|
+
if (!res.ok) {
|
|
165
|
+
throw new Error(`JSON resource ${url} load failed`);
|
|
166
|
+
}
|
|
167
|
+
return res.json();
|
|
168
|
+
})
|
|
169
|
+
.catch(function (error) {
|
|
170
|
+
throw new Error(`JSON resource ${url} load failed: ${error.message}`);
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
173
|
);
|
|
174
174
|
|
|
175
175
|
/**
|
|
@@ -178,17 +178,17 @@ loaderMap.set(
|
|
|
178
178
|
* @param {LoaderHandler} handler - 加载处理函数,接收url参数,返回Promise
|
|
179
179
|
*/
|
|
180
180
|
function registerLoader(type, handler) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
181
|
+
if (typeof type !== "string" || type.trim() === "") {
|
|
182
|
+
throw new Error("资源类型必须是非空字符串");
|
|
183
|
+
}
|
|
184
|
+
const _ = Object.prototype.toString.call(type).slice(8, -1);
|
|
185
|
+
if (["Function", "AsyncFunction"].includes(_)) {
|
|
186
|
+
throw new Error("加载器必须是一个函数返回Promise对象或一个异步函数");
|
|
187
|
+
}
|
|
188
|
+
if (loaderMap.has(type)) {
|
|
189
|
+
console.warn(`类型为${type}的加载器已存在,将被覆盖`);
|
|
190
|
+
}
|
|
191
|
+
loaderMap.set(type, handler);
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
/**
|
|
@@ -197,56 +197,64 @@ function registerLoader(type, handler) {
|
|
|
197
197
|
* @returns {Function} 加载器处理函数
|
|
198
198
|
*/
|
|
199
199
|
function getLoader(type) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
200
|
+
if (!loaderMap.has(type)) {
|
|
201
|
+
throw new Error(`未找到${type}类型的加载器,请先注册自定义加载器`);
|
|
202
|
+
}
|
|
203
|
+
return loaderMap.get(type);
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
/**
|
|
207
|
-
*
|
|
208
|
-
* @param {Array} configList - 完整配置数组
|
|
209
|
-
* @param {number} currentId - 当前资源ID
|
|
210
|
-
* @param {Set} visited - 已访问的资源ID集合
|
|
211
|
-
* @param {Set} visiting - 正在访问的资源ID集合(用于检测环)
|
|
207
|
+
* 预检测所有资源的循环依赖
|
|
208
|
+
* @param {Array<FullResourceConfig>} configList - 完整配置数组
|
|
212
209
|
*/
|
|
213
|
-
function
|
|
214
|
-
|
|
210
|
+
function checkDependencies (configList) {
|
|
211
|
+
// 预构建配置映射,O(n) 时间复杂度
|
|
212
|
+
const configMap = new Map(configList.map((config) => [config.name, config]));
|
|
213
|
+
|
|
214
|
+
const visited = new Set();
|
|
215
|
+
const visiting = new Set(); // 全局共享 visiting 集合
|
|
216
|
+
|
|
217
|
+
for (const config of configList) {
|
|
218
|
+
if (!visited.has(config.name)) {
|
|
219
|
+
detectCycle(configMap, config.name);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* 检测循环依赖
|
|
224
|
+
* @param {Map} configMap - 预构建的配置映射
|
|
225
|
+
* @param {string|number} currentId - 当前资源ID
|
|
226
|
+
*/
|
|
227
|
+
function detectCycle(configMap, currentId) {
|
|
228
|
+
const currentConfig = configMap.get(currentId);
|
|
215
229
|
if (!currentConfig) {
|
|
216
|
-
|
|
230
|
+
throw new Error(`未找到ID为${currentId}的资源配置`);
|
|
217
231
|
}
|
|
218
232
|
|
|
219
233
|
// 若正在访问中,说明存在循环依赖
|
|
220
234
|
if (visiting.has(currentId)) {
|
|
221
|
-
|
|
235
|
+
throw new Error(
|
|
236
|
+
`检测到循环依赖:${Array.from(visiting).join(" -> ")} -> ${currentId}`,
|
|
237
|
+
);
|
|
222
238
|
}
|
|
223
239
|
|
|
224
|
-
//
|
|
240
|
+
// 若已访问过,直接返回
|
|
225
241
|
if (visited.has(currentId)) {
|
|
226
|
-
|
|
242
|
+
return;
|
|
227
243
|
}
|
|
228
244
|
|
|
229
245
|
// 标记为正在访问
|
|
230
246
|
visiting.add(currentId);
|
|
231
|
-
|
|
247
|
+
|
|
248
|
+
// 递归检测依赖项
|
|
232
249
|
const dependencies = currentConfig.dependencies || [];
|
|
233
250
|
for (const depId of dependencies) {
|
|
234
|
-
|
|
251
|
+
detectCycle(configMap, depId);
|
|
235
252
|
}
|
|
236
|
-
|
|
253
|
+
|
|
254
|
+
// 标记为已访问
|
|
237
255
|
visiting.delete(currentId);
|
|
238
256
|
visited.add(currentId);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* 预检测所有资源的循环依赖(配置字段改为dependencies)
|
|
243
|
-
* @param {Array} configList - 完整配置数组
|
|
244
|
-
*/
|
|
245
|
-
function checkAllCycles(configList) {
|
|
246
|
-
const visited = new Set();
|
|
247
|
-
for (const config of configList) {
|
|
248
|
-
detectCycle(configList, config.name, visited, new Set());
|
|
249
|
-
}
|
|
257
|
+
}
|
|
250
258
|
}
|
|
251
259
|
|
|
252
260
|
/**
|
|
@@ -258,72 +266,77 @@ function checkAllCycles(configList) {
|
|
|
258
266
|
* @returns {Promise<ResourceLoadResult>} 加载结果
|
|
259
267
|
*/
|
|
260
268
|
async function loadResourceWithDeps(name, configList, timeout, loadedMap) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
269
|
+
// 若已加载,直接返回缓存结果
|
|
270
|
+
if (loadedMap.has(name)) {
|
|
271
|
+
return loadedMap.get(name);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const currentConfig = configList.find((item) => item.name === name);
|
|
275
|
+
if (!currentConfig) {
|
|
276
|
+
throw new Error(`未找到name为${name}的资源配置`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// 1. 先加载所有依赖(改为dependencies)
|
|
280
|
+
const dependencies = currentConfig.dependencies || [];
|
|
281
|
+
for (const _ of dependencies) {
|
|
282
|
+
await loadResourceWithDeps(_, configList, timeout, loadedMap);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// 2. 再加载当前资源的urls(顺序加载,一个成功即可)
|
|
286
|
+
const { urls, type } = currentConfig;
|
|
287
|
+
let loadResult = null;
|
|
288
|
+
let status = "success";
|
|
289
|
+
let error = null;
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
loadResult = await loadUrlsInOrder(name, urls, type, timeout);
|
|
293
|
+
} catch (err) {
|
|
294
|
+
status = "failed";
|
|
295
|
+
error = err;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// 3. 缓存加载结果
|
|
299
|
+
const result = {
|
|
300
|
+
name,
|
|
301
|
+
config: currentConfig,
|
|
302
|
+
result: loadResult,
|
|
303
|
+
status,
|
|
304
|
+
error,
|
|
305
|
+
};
|
|
306
|
+
loadedMap.set(name, result);
|
|
307
|
+
return result;
|
|
300
308
|
}
|
|
301
309
|
|
|
302
310
|
/**
|
|
303
311
|
* 按顺序加载urls,只需要一个成功即可
|
|
304
|
-
* @param {
|
|
312
|
+
* @param {string} name - 资源名
|
|
313
|
+
* @param {Array<string>} urls - 资源地址数组
|
|
305
314
|
* @param {string} type - 资源类型
|
|
306
315
|
* @param {number} timeout - 超时时间
|
|
307
316
|
* @returns {Promise<LoadResult>} 成功的加载结果
|
|
308
317
|
*/
|
|
309
|
-
async function loadUrlsInOrder(urls, type, timeout) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
|
|
318
|
+
async function loadUrlsInOrder(name, urls, type, timeout) {
|
|
319
|
+
if (!Array.isArray(urls) || urls.length === 0) {
|
|
320
|
+
throw new Error("urls必须是非空数组");
|
|
321
|
+
}
|
|
322
|
+
const errors = [];
|
|
323
|
+
|
|
324
|
+
// 按顺序遍历urls,直到有一个加载成功
|
|
325
|
+
for (const url of urls) {
|
|
326
|
+
try {
|
|
327
|
+
const ret = await wrapLoadPromise(url, type, timeout);
|
|
328
|
+
if (errors.length) {
|
|
329
|
+
console.warn(`加载${name}资源时的一些错误信息:`, errors);
|
|
330
|
+
}
|
|
331
|
+
return ret;
|
|
332
|
+
} catch (error) {
|
|
333
|
+
errors.push({ url, error });
|
|
323
334
|
}
|
|
335
|
+
}
|
|
324
336
|
|
|
325
|
-
|
|
326
|
-
|
|
337
|
+
// 所有url都加载失败,抛出最后一个错误
|
|
338
|
+
console.warn(`所有${name}资源加载错误信息`, errors);
|
|
339
|
+
throw new Error(`所有${name}资源加载失败,错误内容看上面`);
|
|
327
340
|
}
|
|
328
341
|
|
|
329
342
|
/**
|
|
@@ -332,55 +345,66 @@ async function loadUrlsInOrder(urls, type, timeout) {
|
|
|
332
345
|
* @returns {Promise<ResourceLoadResult[]>} 按配置定义顺序的加载结果数组
|
|
333
346
|
*/
|
|
334
347
|
async function resourcePreloader(configList) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return {
|
|
354
|
-
name: url.pathname,
|
|
355
|
-
urls: item,
|
|
356
|
-
type: url.pathname.split('.').pop() || 'js',
|
|
357
|
-
};
|
|
358
|
-
} else {
|
|
359
|
-
return item;
|
|
348
|
+
if (!Array.isArray(configList) || configList.length === 0) {
|
|
349
|
+
throw new Error("配置数组必须是非空数组");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// 将 string[]|string 形式处理成 { name, urls, type } 形式 同时处理 urls:string 为 urls:[string]
|
|
353
|
+
/** @type {Array<FullResourceConfig>} */
|
|
354
|
+
const configs = configList
|
|
355
|
+
.map(function(item) {
|
|
356
|
+
if (typeof item === "string") {
|
|
357
|
+
const url = new URL(item, window.location.href);
|
|
358
|
+
return {
|
|
359
|
+
name: url.pathname,
|
|
360
|
+
urls: [item],
|
|
361
|
+
type: url.pathname.split(".").pop() || "js",
|
|
362
|
+
};
|
|
363
|
+
} else if (Array.isArray(item)) {
|
|
364
|
+
if (!item.length) {
|
|
365
|
+
return;
|
|
360
366
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const loadedMap = new Map();
|
|
371
|
-
|
|
372
|
-
// 4. 按配置定义的顺序,并行加载同层级无依赖资源(保证依赖先加载,结果保留原始顺序)
|
|
373
|
-
const rawOrderPromises = configList.map(async function(config) {
|
|
374
|
-
return await loadResourceWithDeps(config.name, configList, globalTimeout, loadedMap);
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
// 5. 等待所有资源加载完成,返回原始配置顺序的结果
|
|
378
|
-
return Promise.all(rawOrderPromises).then(function (result) {
|
|
379
|
-
if (result.find(item => item.error)) {
|
|
380
|
-
return Promise.reject(result);
|
|
367
|
+
const url = new URL(item[0], window.location.href);
|
|
368
|
+
return {
|
|
369
|
+
name: url.pathname,
|
|
370
|
+
urls: item,
|
|
371
|
+
type: url.pathname.split(".").pop() || "js",
|
|
372
|
+
};
|
|
373
|
+
} else {
|
|
374
|
+
if (typeof item.urls === "string") {
|
|
375
|
+
item.urls = [item.urls];
|
|
381
376
|
}
|
|
382
|
-
return
|
|
383
|
-
|
|
377
|
+
return item;
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
.filter(Boolean);
|
|
381
|
+
|
|
382
|
+
// 1. 获取全局配置
|
|
383
|
+
const { timeout: globalTimeout } = getGlobalConfig();
|
|
384
|
+
|
|
385
|
+
// 2. 预检测所有循环依赖(防止无限递归)
|
|
386
|
+
checkDependencies(configs);
|
|
387
|
+
|
|
388
|
+
// 3. 初始化缓存(存储已加载资源结果,避免重复加载)
|
|
389
|
+
const loadedMap = new Map();
|
|
390
|
+
|
|
391
|
+
// 4. 按配置定义的顺序,并行加载同层级无依赖资源(保证依赖先加载,结果保留原始顺序)
|
|
392
|
+
const rawOrderPromises = configs.map(async function (config) {
|
|
393
|
+
return await loadResourceWithDeps(
|
|
394
|
+
config.name,
|
|
395
|
+
configs,
|
|
396
|
+
globalTimeout,
|
|
397
|
+
loadedMap,
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// 5. 等待所有资源加载完成,返回原始配置顺序的结果
|
|
402
|
+
return Promise.all(rawOrderPromises).then(function (result) {
|
|
403
|
+
if (result.find((item) => item.error)) {
|
|
404
|
+
return Promise.reject(result);
|
|
405
|
+
}
|
|
406
|
+
return result;
|
|
407
|
+
});
|
|
384
408
|
}
|
|
385
409
|
|
|
386
410
|
export { resourcePreloader as default, registerLoader, resourcePreloader, setGlobalConfig };
|