network-speed-js 1.0.4 → 1.0.6
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/README.md +156 -30
- package/dist/core/sdk.d.ts +36 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/speed-tester.d.ts +17 -2
- package/dist/core/speed-tester.d.ts.map +1 -1
- package/dist/core/types.d.ts +27 -16
- package/dist/core/types.d.ts.map +1 -1
- package/dist/network-speed-js.js +133 -73
- package/dist/network-speed-js.umd.js +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Network Speed SDK
|
|
2
|
-
|
|
3
1
|
<div align="center">
|
|
4
2
|
|
|
3
|
+
# Network Speed SDK
|
|
4
|
+
|
|
5
5
|
一个基于 **Performance API** 的现代化网速测试 SDK,支持内外网自动检测、资源监听和完整的 TS 类型支持。
|
|
6
6
|
|
|
7
7
|
**框架无关 · 开箱即用 · 准确可靠**
|
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
<div align="center">
|
|
17
|
+
|
|
16
18
|
## 🎯 核心亮点
|
|
17
19
|
|
|
20
|
+
</div>
|
|
21
|
+
|
|
18
22
|
### 从 Axios 到 Performance API 的技术升级
|
|
19
23
|
|
|
20
24
|
本项目从 **v0.x(Axios 拦截器方案)** 完全重构为 **v1.0(Performance API 方案)**,实现了测速准确度和功能的质的飞跃。
|
|
@@ -33,8 +37,12 @@
|
|
|
33
37
|
|
|
34
38
|
---
|
|
35
39
|
|
|
40
|
+
<div align="center">
|
|
41
|
+
|
|
36
42
|
## ✨ 特性
|
|
37
43
|
|
|
44
|
+
</div>
|
|
45
|
+
|
|
38
46
|
- 🚀 **基于 Performance API** - 使用浏览器原生 API,准确测量真实下载速度
|
|
39
47
|
- 🔄 **内外网自动检测** - 智能切换内网/外网测速资源
|
|
40
48
|
- 📊 **完整的性能数据** - 提供速度、耗时、传输大小等详细信息
|
|
@@ -43,21 +51,49 @@
|
|
|
43
51
|
- 🌐 **框架无关** - 可用于 Vue、React、Angular 或原生 JavaScript 项目
|
|
44
52
|
- 📦 **轻量级** - 零依赖,体积小巧
|
|
45
53
|
|
|
54
|
+
<div align="center">
|
|
55
|
+
|
|
46
56
|
## 📦 安装
|
|
47
57
|
|
|
58
|
+
</div>
|
|
59
|
+
|
|
48
60
|
```bash
|
|
49
61
|
npm install network-speed-js
|
|
50
62
|
```
|
|
51
63
|
|
|
64
|
+
<div align="center">
|
|
65
|
+
|
|
52
66
|
## 🚀 快速开始
|
|
53
67
|
|
|
68
|
+
</div>
|
|
69
|
+
|
|
54
70
|
### 原生 JavaScript / TypeScript
|
|
55
71
|
|
|
72
|
+
**方式一:使用图片资源测速(推荐,默认模式,无需CORS)**
|
|
73
|
+
|
|
56
74
|
```typescript
|
|
57
75
|
import { NetworkSpeedSDK } from 'network-speed-js';
|
|
58
76
|
|
|
59
77
|
const sdk = new NetworkSpeedSDK({
|
|
60
|
-
|
|
78
|
+
internetImageUrl: 'https://cdn.example.com/test-image.jpg',
|
|
79
|
+
// 可选:内网图片URL
|
|
80
|
+
intranetImageUrl: 'https://internal-cdn.com/test-image.jpg',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const result = await sdk.test();
|
|
84
|
+
console.log(`网速: ${result.speedMbps} Mbps`);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**方式二:使用任意资源测速(需要CORS支持)**
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { NetworkSpeedSDK } from 'network-speed-js';
|
|
91
|
+
|
|
92
|
+
const sdk = new NetworkSpeedSDK({
|
|
93
|
+
internetUrl: 'https://cdn.example.com/test-file.bin',
|
|
94
|
+
useFetch: true, // 启用fetch模式
|
|
95
|
+
// 可选:内网资源URL
|
|
96
|
+
intranetUrl: 'https://internal-cdn.com/test-file.bin',
|
|
61
97
|
});
|
|
62
98
|
|
|
63
99
|
const result = await sdk.test();
|
|
@@ -84,7 +120,7 @@ const result = ref(null);
|
|
|
84
120
|
const testSpeed = async () => {
|
|
85
121
|
loading.value = true;
|
|
86
122
|
const sdk = new NetworkSpeedSDK({
|
|
87
|
-
|
|
123
|
+
internetImageUrl: 'https://cdn.example.com/test-image.jpg',
|
|
88
124
|
});
|
|
89
125
|
result.value = await sdk.test();
|
|
90
126
|
loading.value = false;
|
|
@@ -105,7 +141,7 @@ function SpeedTest() {
|
|
|
105
141
|
const testSpeed = async () => {
|
|
106
142
|
setLoading(true);
|
|
107
143
|
const sdk = new NetworkSpeedSDK({
|
|
108
|
-
|
|
144
|
+
internetImageUrl: 'https://cdn.example.com/test-image.jpg',
|
|
109
145
|
});
|
|
110
146
|
const data = await sdk.test();
|
|
111
147
|
setResult(data);
|
|
@@ -145,7 +181,7 @@ export class SpeedTestComponent {
|
|
|
145
181
|
async testSpeed() {
|
|
146
182
|
this.loading = true;
|
|
147
183
|
const sdk = new NetworkSpeedSDK({
|
|
148
|
-
|
|
184
|
+
internetImageUrl: 'https://cdn.example.com/test-image.jpg',
|
|
149
185
|
});
|
|
150
186
|
this.result = await sdk.test();
|
|
151
187
|
this.loading = false;
|
|
@@ -153,8 +189,12 @@ export class SpeedTestComponent {
|
|
|
153
189
|
}
|
|
154
190
|
```
|
|
155
191
|
|
|
192
|
+
<div align="center">
|
|
193
|
+
|
|
156
194
|
## 📖 API 文档
|
|
157
195
|
|
|
196
|
+
</div>
|
|
197
|
+
|
|
158
198
|
### NetworkSpeedSDK
|
|
159
199
|
|
|
160
200
|
#### 构造函数
|
|
@@ -163,29 +203,63 @@ export class SpeedTestComponent {
|
|
|
163
203
|
new NetworkSpeedSDK(options?: SpeedTestOptions)
|
|
164
204
|
```
|
|
165
205
|
|
|
166
|
-
|
|
206
|
+
**参数说明:**
|
|
207
|
+
- `options`(可选):配置选项
|
|
208
|
+
- 如果传入配置,可以执行测速
|
|
209
|
+
- 如果不传配置,只能使用工具函数(`getAllResourcesSpeeds`、`observeResource`)
|
|
167
210
|
|
|
168
|
-
|
|
169
|
-
|------|------|--------|------|
|
|
170
|
-
| `intranetUrl` | `string` | `''` | 内网测速资源URL |
|
|
171
|
-
| `internetUrl` | `string` | `''` | 外网测速资源URL(必填) |
|
|
172
|
-
| `autoDetect` | `boolean` | `true` | 是否自动检测内外网 |
|
|
173
|
-
| `timeout` | `number` | `10000` | 超时时间 (ms) |
|
|
174
|
-
| `thresholds` | `object` | `{fast: 10, medium: 2}` | 网速评估阈值 (Mbps) |
|
|
175
|
-
| `resourceType` | `'image' \| 'fetch'` | `'image'` | 资源类型(见下方说明) |
|
|
211
|
+
**配置选项(两种模式):**
|
|
176
212
|
|
|
177
|
-
|
|
213
|
+
**模式一:图片模式(默认,推荐)**
|
|
178
214
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
215
|
+
```typescript
|
|
216
|
+
interface ImageSpeedTestOptions {
|
|
217
|
+
internetImageUrl: string; // ✅ 必填:外网图片URL
|
|
218
|
+
intranetImageUrl?: string; // 可选:内网图片URL
|
|
219
|
+
timeout?: number; // 可选:超时时间 (ms),默认 10000
|
|
220
|
+
autoDetect?: boolean; // 可选:是否自动检测内外网,默认 true
|
|
221
|
+
thresholds?: { // 可选:网速评估阈值 (Mbps)
|
|
222
|
+
fast: number; // 默认 10
|
|
223
|
+
medium: number; // 默认 2
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
```
|
|
184
227
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
228
|
+
**模式二:Fetch模式(支持任意资源,需要CORS)**
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
interface FetchSpeedTestOptions {
|
|
232
|
+
internetUrl: string; // ✅ 必填:外网资源URL
|
|
233
|
+
useFetch: true; // ✅ 必填:启用fetch模式
|
|
234
|
+
intranetUrl?: string; // 可选:内网资源URL
|
|
235
|
+
timeout?: number; // 可选:超时时间 (ms),默认 10000
|
|
236
|
+
autoDetect?: boolean; // 可选:是否自动检测内外网,默认 true
|
|
237
|
+
thresholds?: { // 可选:网速评估阈值 (Mbps)
|
|
238
|
+
fast: number; // 默认 10
|
|
239
|
+
medium: number; // 默认 2
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**使用示例:**
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
// ✅ 图片模式(只需要图片URL)
|
|
248
|
+
const sdk1 = new NetworkSpeedSDK({
|
|
249
|
+
internetImageUrl: 'https://cdn.example.com/test.jpg',
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// ✅ Fetch模式(需要URL + useFetch: true)
|
|
253
|
+
const sdk2 = new NetworkSpeedSDK({
|
|
254
|
+
internetUrl: 'https://cdn.example.com/test.bin',
|
|
255
|
+
useFetch: true,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// ✅ 无参数实例化(仅用于工具函数)
|
|
259
|
+
const sdk3 = new NetworkSpeedSDK();
|
|
260
|
+
const speeds = sdk3.getAllResourcesSpeeds(); // ✅ 可以使用
|
|
261
|
+
await sdk3.test(); // ❌ 会抛出错误:SDK未配置
|
|
262
|
+
```
|
|
189
263
|
|
|
190
264
|
#### 方法
|
|
191
265
|
|
|
@@ -277,14 +351,17 @@ interface ResourceSpeedInfo {
|
|
|
277
351
|
}
|
|
278
352
|
```
|
|
279
353
|
|
|
354
|
+
<div align="center">
|
|
355
|
+
|
|
280
356
|
## 💡 使用示例
|
|
281
357
|
|
|
358
|
+
</div>
|
|
359
|
+
|
|
282
360
|
### 1. 使用图片资源测速(默认,推荐)
|
|
283
361
|
|
|
284
362
|
```typescript
|
|
285
363
|
const sdk = new NetworkSpeedSDK({
|
|
286
|
-
|
|
287
|
-
// resourceType: 'image', // 默认值,可省略
|
|
364
|
+
internetImageUrl: 'https://cdn.example.com/test-image.jpg',
|
|
288
365
|
});
|
|
289
366
|
|
|
290
367
|
const result = await sdk.test();
|
|
@@ -295,7 +372,7 @@ const result = await sdk.test();
|
|
|
295
372
|
```typescript
|
|
296
373
|
const sdk = new NetworkSpeedSDK({
|
|
297
374
|
internetUrl: 'https://cdn.example.com/test-file.bin',
|
|
298
|
-
|
|
375
|
+
useFetch: true, // 必须启用fetch模式
|
|
299
376
|
});
|
|
300
377
|
|
|
301
378
|
const result = await sdk.test();
|
|
@@ -304,9 +381,18 @@ const result = await sdk.test();
|
|
|
304
381
|
### 3. 内外网自动检测
|
|
305
382
|
|
|
306
383
|
```typescript
|
|
384
|
+
// 图片模式
|
|
307
385
|
const sdk = new NetworkSpeedSDK({
|
|
308
|
-
|
|
309
|
-
|
|
386
|
+
intranetImageUrl: 'https://internal-cdn.company.com/test.jpg',
|
|
387
|
+
internetImageUrl: 'https://public-cdn.example.com/test.jpg',
|
|
388
|
+
autoDetect: true,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// 或 Fetch模式
|
|
392
|
+
const sdk = new NetworkSpeedSDK({
|
|
393
|
+
intranetUrl: 'https://internal-cdn.company.com/test.bin',
|
|
394
|
+
internetUrl: 'https://public-cdn.example.com/test.bin',
|
|
395
|
+
useFetch: true,
|
|
310
396
|
autoDetect: true,
|
|
311
397
|
});
|
|
312
398
|
|
|
@@ -417,8 +503,12 @@ const monitor = new NetworkMonitor();
|
|
|
417
503
|
setInterval(() => monitor.monitor(), 60000); // 每分钟检测
|
|
418
504
|
```
|
|
419
505
|
|
|
506
|
+
<div align="center">
|
|
507
|
+
|
|
420
508
|
## 🎨 框架集成
|
|
421
509
|
|
|
510
|
+
</div>
|
|
511
|
+
|
|
422
512
|
### Vue 3 自定义 Hook
|
|
423
513
|
|
|
424
514
|
```typescript
|
|
@@ -494,8 +584,12 @@ export function useNetworkSpeed(options = {}) {
|
|
|
494
584
|
}
|
|
495
585
|
```
|
|
496
586
|
|
|
587
|
+
<div align="center">
|
|
588
|
+
|
|
497
589
|
## ⚙️ 配置指南
|
|
498
590
|
|
|
591
|
+
</div>
|
|
592
|
+
|
|
499
593
|
### 测速资源准备
|
|
500
594
|
|
|
501
595
|
#### 服务端配置(Nginx)
|
|
@@ -579,8 +673,12 @@ dd if=/dev/urandom of=speed-test.bin bs=1024 count=500
|
|
|
579
673
|
}
|
|
580
674
|
```
|
|
581
675
|
|
|
676
|
+
<div align="center">
|
|
677
|
+
|
|
582
678
|
## 🔍 核心概念
|
|
583
679
|
|
|
680
|
+
</div>
|
|
681
|
+
|
|
584
682
|
### Performance API 是什么?
|
|
585
683
|
|
|
586
684
|
Performance API 是浏览器提供的原生性能监控接口,可以精确测量资源加载的各个阶段耗时。
|
|
@@ -602,8 +700,12 @@ Performance API 是浏览器提供的原生性能监控接口,可以精确测
|
|
|
602
700
|
2. 如果内网资源超时或失败,自动切换到外网资源
|
|
603
701
|
3. 根据成功的资源判断当前网络环境
|
|
604
702
|
|
|
703
|
+
<div align="center">
|
|
704
|
+
|
|
605
705
|
## 📊 Performance API vs 其他方案
|
|
606
706
|
|
|
707
|
+
</div>
|
|
708
|
+
|
|
607
709
|
| 方案 | 准确度 | 可控性 | 复杂度 | 推荐 |
|
|
608
710
|
|------|--------|--------|--------|------|
|
|
609
711
|
| Performance API | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | ✅ 主力 |
|
|
@@ -642,8 +744,12 @@ const size = entry.transferSize; // 真实传输字节数
|
|
|
642
744
|
- ✅ 可识别缓存命中
|
|
643
745
|
- ✅ 提供完整的加载时序
|
|
644
746
|
|
|
747
|
+
<div align="center">
|
|
748
|
+
|
|
645
749
|
## ❓ 常见问题
|
|
646
750
|
|
|
751
|
+
</div>
|
|
752
|
+
|
|
647
753
|
### Q1: 测速结果不准确?
|
|
648
754
|
|
|
649
755
|
**可能原因:**
|
|
@@ -718,8 +824,12 @@ if (connection) {
|
|
|
718
824
|
}
|
|
719
825
|
```
|
|
720
826
|
|
|
827
|
+
<div align="center">
|
|
828
|
+
|
|
721
829
|
## 🚀 性能优化
|
|
722
830
|
|
|
831
|
+
</div>
|
|
832
|
+
|
|
723
833
|
### 1. 避免频繁测速
|
|
724
834
|
|
|
725
835
|
```typescript
|
|
@@ -783,8 +893,12 @@ async function testSpeed() {
|
|
|
783
893
|
}
|
|
784
894
|
```
|
|
785
895
|
|
|
896
|
+
<div align="center">
|
|
897
|
+
|
|
786
898
|
## ⚠️ 注意事项
|
|
787
899
|
|
|
900
|
+
</div>
|
|
901
|
+
|
|
788
902
|
### Performance API 能做什么
|
|
789
903
|
|
|
790
904
|
✅ 基于真实资源加载评估下载速度
|
|
@@ -799,8 +913,12 @@ async function testSpeed() {
|
|
|
799
913
|
❌ 测量 RTT 抖动
|
|
800
914
|
❌ 脱离真实请求独立测速
|
|
801
915
|
|
|
916
|
+
<div align="center">
|
|
917
|
+
|
|
802
918
|
## 🤝 贡献
|
|
803
919
|
|
|
920
|
+
</div>
|
|
921
|
+
|
|
804
922
|
欢迎提交 Issue 和 Pull Request!
|
|
805
923
|
|
|
806
924
|
### 本地开发
|
|
@@ -839,12 +957,20 @@ network-speed-js/
|
|
|
839
957
|
└── README.md # 项目文档
|
|
840
958
|
```
|
|
841
959
|
|
|
960
|
+
<div align="center">
|
|
961
|
+
|
|
842
962
|
## 📄 License
|
|
843
963
|
|
|
964
|
+
</div>
|
|
965
|
+
|
|
844
966
|
MIT License
|
|
845
967
|
|
|
968
|
+
<div align="center">
|
|
969
|
+
|
|
846
970
|
## 🔗 相关链接
|
|
847
971
|
|
|
972
|
+
</div>
|
|
973
|
+
|
|
848
974
|
- [更新日志 (CHANGELOG)](./CHANGELOG.md)
|
|
849
975
|
- [Performance API 文档](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API)
|
|
850
976
|
- [PerformanceResourceTiming](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming)
|
package/dist/core/sdk.d.ts
CHANGED
|
@@ -4,23 +4,51 @@ import type { SpeedTestOptions, SpeedTestResult, ResourceSpeedInfo } from './typ
|
|
|
4
4
|
*/
|
|
5
5
|
export declare class NetworkSpeedSDK {
|
|
6
6
|
private tester;
|
|
7
|
+
/**
|
|
8
|
+
* 创建SDK实例
|
|
9
|
+
* @param options 配置选项(可选,如果不传则只能使用工具函数,不能执行测速)
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // 图片模式(默认,推荐)
|
|
13
|
+
* const sdk = new NetworkSpeedSDK({
|
|
14
|
+
* internetImageUrl: 'https://cdn.example.com/test.jpg',
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Fetch模式(需要CORS)
|
|
19
|
+
* const sdk = new NetworkSpeedSDK({
|
|
20
|
+
* internetUrl: 'https://cdn.example.com/test.bin',
|
|
21
|
+
* useFetch: true,
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // 仅使用工具函数
|
|
26
|
+
* const sdk = new NetworkSpeedSDK();
|
|
27
|
+
* const speeds = sdk.getAllResourcesSpeeds();
|
|
28
|
+
*/
|
|
7
29
|
constructor(options?: SpeedTestOptions);
|
|
8
30
|
/**
|
|
9
31
|
* 执行网速测试
|
|
32
|
+
* @throws {Error} 如果SDK未配置
|
|
10
33
|
*/
|
|
11
34
|
test(): Promise<SpeedTestResult>;
|
|
12
35
|
/**
|
|
13
36
|
* 获取所有已加载资源的速度信息
|
|
37
|
+
* @returns 资源速度信息数组
|
|
14
38
|
*/
|
|
15
39
|
getAllResourcesSpeeds(): ResourceSpeedInfo[];
|
|
16
40
|
/**
|
|
17
41
|
* 监听特定资源的性能数据
|
|
42
|
+
* @param urlPattern URL匹配模式
|
|
43
|
+
* @param callback 回调函数
|
|
44
|
+
* @returns 停止监听的函数
|
|
18
45
|
*/
|
|
19
46
|
observeResource(urlPattern: string, callback: (entry: PerformanceResourceTiming) => void): () => void;
|
|
20
47
|
/**
|
|
21
48
|
* 更新配置
|
|
49
|
+
* @param options 新的配置选项
|
|
22
50
|
*/
|
|
23
|
-
updateOptions(options:
|
|
51
|
+
updateOptions(options: SpeedTestOptions): void;
|
|
24
52
|
/**
|
|
25
53
|
* 销毁SDK实例
|
|
26
54
|
*/
|
|
@@ -28,6 +56,13 @@ export declare class NetworkSpeedSDK {
|
|
|
28
56
|
}
|
|
29
57
|
/**
|
|
30
58
|
* 创建SDK实例的工厂函数
|
|
59
|
+
* @param options 配置选项
|
|
60
|
+
* @returns SDK实例
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* const sdk = createNetworkSpeedSDK({
|
|
64
|
+
* internetImageUrl: 'https://cdn.example.com/test.jpg',
|
|
65
|
+
* });
|
|
31
66
|
*/
|
|
32
67
|
export declare function createNetworkSpeedSDK(options?: SpeedTestOptions): NetworkSpeedSDK;
|
|
33
68
|
export default NetworkSpeedSDK;
|
package/dist/core/sdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEpF;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/core/sdk.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEpF;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAA4B;IAE1C;;;;;;;;;;;;;;;;;;;;;OAqBG;gBACS,OAAO,CAAC,EAAE,gBAAgB;IAMtC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC;IAatC;;;OAGG;IACH,qBAAqB,IAAI,iBAAiB,EAAE;IAI5C;;;;;OAKG;IACH,eAAe,CACb,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,IAAI,GACnD,MAAM,IAAI;IAUb;;;OAGG;IACH,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAK9C;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAEjF;AAGD,eAAe,eAAe,CAAC"}
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import type { SpeedTestResult, SpeedTestOptions, PerformanceEntryCallback } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* 内部标准化配置
|
|
4
|
+
*/
|
|
5
|
+
interface NormalizedOptions {
|
|
6
|
+
intranetUrl: string;
|
|
7
|
+
internetUrl: string;
|
|
8
|
+
timeout: number;
|
|
9
|
+
autoDetect: boolean;
|
|
10
|
+
thresholds: {
|
|
11
|
+
fast: number;
|
|
12
|
+
medium: number;
|
|
13
|
+
};
|
|
14
|
+
useFetch: boolean;
|
|
15
|
+
}
|
|
2
16
|
/**
|
|
3
17
|
* 网速测试核心类
|
|
4
18
|
*/
|
|
5
19
|
export declare class SpeedTester {
|
|
6
20
|
private options;
|
|
7
21
|
private observer;
|
|
8
|
-
constructor(options
|
|
22
|
+
constructor(options: SpeedTestOptions);
|
|
9
23
|
/**
|
|
10
24
|
* 执行测速
|
|
11
25
|
*/
|
|
@@ -33,10 +47,11 @@ export declare class SpeedTester {
|
|
|
33
47
|
/**
|
|
34
48
|
* 更新配置
|
|
35
49
|
*/
|
|
36
|
-
updateOptions(options: Partial<
|
|
50
|
+
updateOptions(options: Partial<NormalizedOptions>): void;
|
|
37
51
|
/**
|
|
38
52
|
* 销毁实例
|
|
39
53
|
*/
|
|
40
54
|
destroy(): void;
|
|
41
55
|
}
|
|
56
|
+
export {};
|
|
42
57
|
//# sourceMappingURL=speed-tester.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"speed-tester.d.ts","sourceRoot":"","sources":["../../src/core/speed-tester.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACzB,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"speed-tester.d.ts","sourceRoot":"","sources":["../../src/core/speed-tester.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACzB,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,UAAU,iBAAiB;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,QAAQ,CAAoC;gBAExC,OAAO,EAAE,gBAAgB;IAgDrC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC;IAQtC;;OAEG;YACW,kBAAkB;IAehC;;OAEG;IACH,OAAO,CAAC,aAAa;IA0DrB;;OAEG;IACH,OAAO,CAAC,aAAa;IAqBrB;;OAEG;IACH,OAAO,CAAC,aAAa;IA4BrB;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,wBAAwB,GAAG,MAAM,IAAI;IAkBnF;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAIxD;;OAEG;IACH,OAAO,IAAI,IAAI;CAMhB"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -33,17 +33,9 @@ export interface ResourceSpeedInfo {
|
|
|
33
33
|
transferSize: number;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* 基础配置选项
|
|
37
37
|
*/
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* SDK配置选项
|
|
41
|
-
*/
|
|
42
|
-
export interface SpeedTestOptions {
|
|
43
|
-
/** 内网测速资源URL */
|
|
44
|
-
intranetUrl?: string;
|
|
45
|
-
/** 外网测速资源URL */
|
|
46
|
-
internetUrl?: string;
|
|
38
|
+
interface BaseSpeedTestOptions {
|
|
47
39
|
/** 超时时间 (ms) */
|
|
48
40
|
timeout?: number;
|
|
49
41
|
/** 是否自动检测内外网 */
|
|
@@ -53,17 +45,36 @@ export interface SpeedTestOptions {
|
|
|
53
45
|
fast: number;
|
|
54
46
|
medium: number;
|
|
55
47
|
};
|
|
56
|
-
/**
|
|
57
|
-
* 资源类型
|
|
58
|
-
* - 'image': 使用 Image 对象加载(默认,不受跨域限制,适用于图片资源)
|
|
59
|
-
* - 'fetch': 使用 fetch API 加载(需要服务器支持 CORS,适用于任意资源)
|
|
60
|
-
*/
|
|
61
|
-
resourceType?: ResourceType;
|
|
62
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* 图片模式配置(默认,不受跨域限制)
|
|
51
|
+
*/
|
|
52
|
+
export interface ImageSpeedTestOptions extends BaseSpeedTestOptions {
|
|
53
|
+
/** 内网测速图片URL */
|
|
54
|
+
intranetImageUrl?: string;
|
|
55
|
+
/** 外网测速图片URL(必填) */
|
|
56
|
+
internetImageUrl: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Fetch模式配置(支持任意资源,需要CORS)
|
|
60
|
+
*/
|
|
61
|
+
export interface FetchSpeedTestOptions extends BaseSpeedTestOptions {
|
|
62
|
+
/** 内网测速资源URL */
|
|
63
|
+
intranetUrl?: string;
|
|
64
|
+
/** 外网测速资源URL(必填) */
|
|
65
|
+
internetUrl: string;
|
|
66
|
+
/** 使用fetch模式 */
|
|
67
|
+
useFetch: true;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* SDK配置选项(联合类型)
|
|
71
|
+
*/
|
|
72
|
+
export type SpeedTestOptions = ImageSpeedTestOptions | FetchSpeedTestOptions;
|
|
63
73
|
/**
|
|
64
74
|
* Performance Observer 回调参数
|
|
65
75
|
*/
|
|
66
76
|
export interface PerformanceEntryCallback {
|
|
67
77
|
(entry: PerformanceResourceTiming): void;
|
|
68
78
|
}
|
|
79
|
+
export {};
|
|
69
80
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/core/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,WAAW,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD,YAAY;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,WAAW,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD,YAAY;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,UAAU,oBAAoB;IAC5B,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oBAAoB;IACpB,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,gBAAgB;IAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB;IACpB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB;IAChB,QAAQ,EAAE,IAAI,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,CAAC,KAAK,EAAE,yBAAyB,GAAG,IAAI,CAAC;CAC1C"}
|
package/dist/network-speed-js.js
CHANGED
|
@@ -1,41 +1,63 @@
|
|
|
1
|
-
function
|
|
1
|
+
function h(t) {
|
|
2
2
|
const e = t.responseEnd - t.responseStart;
|
|
3
3
|
if (e <= 0 || t.transferSize === 0)
|
|
4
4
|
return null;
|
|
5
|
-
const
|
|
5
|
+
const r = t.transferSize * 8 / e / 1e3, n = t.transferSize / e;
|
|
6
6
|
return {
|
|
7
7
|
name: t.name,
|
|
8
|
-
speedMbps: Number(
|
|
8
|
+
speedMbps: Number(r.toFixed(2)),
|
|
9
9
|
speedKBps: Number(n.toFixed(2)),
|
|
10
10
|
downloadTime: Number(e.toFixed(2)),
|
|
11
11
|
transferSize: t.transferSize
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
-
function
|
|
14
|
+
function d() {
|
|
15
15
|
return performance.getEntriesByType("resource").filter(
|
|
16
16
|
(e) => e instanceof PerformanceResourceTiming && e.transferSize > 0
|
|
17
|
-
).map(
|
|
17
|
+
).map(h).filter((e) => e !== null);
|
|
18
18
|
}
|
|
19
|
-
function
|
|
19
|
+
function p(t) {
|
|
20
20
|
performance.getEntriesByName(t).forEach(() => {
|
|
21
21
|
performance.clearResourceTimings();
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
-
function
|
|
24
|
+
function w(t, e = { fast: 10, medium: 2 }) {
|
|
25
25
|
return t >= e.fast ? "fast" : t >= e.medium ? "medium" : t > 0 ? "slow" : "unknown";
|
|
26
26
|
}
|
|
27
|
-
class
|
|
27
|
+
class l {
|
|
28
28
|
options;
|
|
29
29
|
observer = null;
|
|
30
|
-
constructor(e
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
constructor(e) {
|
|
31
|
+
if ("useFetch" in e && e.useFetch) {
|
|
32
|
+
if (!e.internetUrl)
|
|
33
|
+
throw new Error("Fetch模式必须提供 internetUrl 参数");
|
|
34
|
+
if (!e.internetUrl.startsWith("http://") && !e.internetUrl.startsWith("https://"))
|
|
35
|
+
throw new Error("internetUrl 必须是完整的HTTP/HTTPS URL");
|
|
36
|
+
this.options = {
|
|
37
|
+
intranetUrl: e.intranetUrl || "",
|
|
38
|
+
internetUrl: e.internetUrl,
|
|
39
|
+
timeout: e.timeout || 1e4,
|
|
40
|
+
autoDetect: e.autoDetect ?? !0,
|
|
41
|
+
thresholds: e.thresholds || { fast: 10, medium: 2 },
|
|
42
|
+
useFetch: !0
|
|
43
|
+
};
|
|
44
|
+
} else {
|
|
45
|
+
const r = "internetImageUrl" in e ? e.internetImageUrl : "";
|
|
46
|
+
if (!r)
|
|
47
|
+
throw new Error("图片模式必须提供 internetImageUrl 参数");
|
|
48
|
+
if (!r.startsWith("http://") && !r.startsWith("https://"))
|
|
49
|
+
throw new Error("internetImageUrl 必须是完整的HTTP/HTTPS URL");
|
|
50
|
+
this.options = {
|
|
51
|
+
intranetUrl: "intranetImageUrl" in e && e.intranetImageUrl || "",
|
|
52
|
+
internetUrl: r,
|
|
53
|
+
timeout: e.timeout || 1e4,
|
|
54
|
+
autoDetect: e.autoDetect ?? !0,
|
|
55
|
+
thresholds: e.thresholds || { fast: 10, medium: 2 },
|
|
56
|
+
useFetch: !1
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (this.options.intranetUrl && !this.options.intranetUrl.startsWith("http://") && !this.options.intranetUrl.startsWith("https://"))
|
|
60
|
+
throw new Error("intranetUrl/intranetImageUrl 必须是完整的HTTP/HTTPS URL");
|
|
39
61
|
}
|
|
40
62
|
/**
|
|
41
63
|
* 执行测速
|
|
@@ -58,74 +80,74 @@ class T {
|
|
|
58
80
|
/**
|
|
59
81
|
* 测试单个URL
|
|
60
82
|
*/
|
|
61
|
-
testSingleUrl(e,
|
|
62
|
-
return new Promise((n,
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
const c = new PerformanceObserver((
|
|
66
|
-
for (const
|
|
67
|
-
if (
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
70
|
-
const
|
|
71
|
-
speedMbps:
|
|
72
|
-
speedKBps:
|
|
73
|
-
networkType:
|
|
74
|
-
|
|
83
|
+
testSingleUrl(e, r) {
|
|
84
|
+
return new Promise((n, i) => {
|
|
85
|
+
const s = `${e}?t=${Date.now()}`;
|
|
86
|
+
p(s);
|
|
87
|
+
const c = new PerformanceObserver((f) => {
|
|
88
|
+
for (const a of f.getEntries())
|
|
89
|
+
if (a.entryType === "resource" && a.name.includes(e)) {
|
|
90
|
+
const o = h(a);
|
|
91
|
+
if (o) {
|
|
92
|
+
const m = {
|
|
93
|
+
speedMbps: o.speedMbps,
|
|
94
|
+
speedKBps: o.speedKBps,
|
|
95
|
+
networkType: w(
|
|
96
|
+
o.speedMbps,
|
|
75
97
|
this.options.thresholds
|
|
76
98
|
),
|
|
77
|
-
isIntranet:
|
|
78
|
-
duration:
|
|
79
|
-
transferSize:
|
|
99
|
+
isIntranet: r,
|
|
100
|
+
duration: o.downloadTime,
|
|
101
|
+
transferSize: o.transferSize,
|
|
80
102
|
resourceUrl: e
|
|
81
103
|
};
|
|
82
|
-
c.disconnect(), n(
|
|
104
|
+
c.disconnect(), n(m);
|
|
83
105
|
}
|
|
84
106
|
}
|
|
85
107
|
});
|
|
86
108
|
c.observe({ entryTypes: ["resource"] });
|
|
87
|
-
const
|
|
88
|
-
c.disconnect(),
|
|
109
|
+
const u = setTimeout(() => {
|
|
110
|
+
c.disconnect(), i(new Error(`测速超时: ${e}`));
|
|
89
111
|
}, this.options.timeout);
|
|
90
|
-
this.options.
|
|
112
|
+
this.options.useFetch ? this.loadWithFetch(s, u, c, i) : this.loadWithImage(s, u, c, i);
|
|
91
113
|
});
|
|
92
114
|
}
|
|
93
115
|
/**
|
|
94
116
|
* 使用 Image 对象加载资源(默认方式,不受跨域限制)
|
|
95
117
|
*/
|
|
96
|
-
loadWithImage(e,
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
clearTimeout(
|
|
100
|
-
},
|
|
101
|
-
clearTimeout(
|
|
102
|
-
},
|
|
118
|
+
loadWithImage(e, r, n, i) {
|
|
119
|
+
const s = new Image();
|
|
120
|
+
s.onload = () => {
|
|
121
|
+
clearTimeout(r);
|
|
122
|
+
}, s.onerror = () => {
|
|
123
|
+
clearTimeout(r), n.disconnect(), i(new Error(`图片加载失败: ${e}`));
|
|
124
|
+
}, s.src = e;
|
|
103
125
|
}
|
|
104
126
|
/**
|
|
105
127
|
* 使用 fetch API 加载资源(需要 CORS 支持)
|
|
106
128
|
*/
|
|
107
|
-
loadWithFetch(e,
|
|
129
|
+
loadWithFetch(e, r, n, i) {
|
|
108
130
|
fetch(e, {
|
|
109
131
|
method: "GET",
|
|
110
132
|
cache: "no-store"
|
|
111
133
|
// 禁用缓存
|
|
112
|
-
}).then((
|
|
113
|
-
if (!
|
|
114
|
-
throw new Error(`HTTP error! status: ${
|
|
115
|
-
return
|
|
134
|
+
}).then((s) => {
|
|
135
|
+
if (!s.ok)
|
|
136
|
+
throw new Error(`HTTP error! status: ${s.status}`);
|
|
137
|
+
return s.blob();
|
|
116
138
|
}).then(() => {
|
|
117
|
-
clearTimeout(
|
|
118
|
-
}).catch((
|
|
119
|
-
clearTimeout(
|
|
139
|
+
clearTimeout(r);
|
|
140
|
+
}).catch((s) => {
|
|
141
|
+
clearTimeout(r), n.disconnect(), i(new Error(`资源加载失败: ${s.message}`));
|
|
120
142
|
});
|
|
121
143
|
}
|
|
122
144
|
/**
|
|
123
145
|
* 监听特定资源的性能数据
|
|
124
146
|
*/
|
|
125
|
-
observeResource(e,
|
|
126
|
-
const n = new PerformanceObserver((
|
|
127
|
-
for (const
|
|
128
|
-
|
|
147
|
+
observeResource(e, r) {
|
|
148
|
+
const n = new PerformanceObserver((i) => {
|
|
149
|
+
for (const s of i.getEntries())
|
|
150
|
+
s.entryType === "resource" && s.name.includes(e) && r(s);
|
|
129
151
|
});
|
|
130
152
|
return n.observe({ entryTypes: ["resource"] }), () => n.disconnect();
|
|
131
153
|
}
|
|
@@ -142,49 +164,87 @@ class T {
|
|
|
142
164
|
this.observer && (this.observer.disconnect(), this.observer = null);
|
|
143
165
|
}
|
|
144
166
|
}
|
|
145
|
-
class
|
|
146
|
-
tester;
|
|
147
|
-
|
|
148
|
-
|
|
167
|
+
class U {
|
|
168
|
+
tester = null;
|
|
169
|
+
/**
|
|
170
|
+
* 创建SDK实例
|
|
171
|
+
* @param options 配置选项(可选,如果不传则只能使用工具函数,不能执行测速)
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* // 图片模式(默认,推荐)
|
|
175
|
+
* const sdk = new NetworkSpeedSDK({
|
|
176
|
+
* internetImageUrl: 'https://cdn.example.com/test.jpg',
|
|
177
|
+
* });
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* // Fetch模式(需要CORS)
|
|
181
|
+
* const sdk = new NetworkSpeedSDK({
|
|
182
|
+
* internetUrl: 'https://cdn.example.com/test.bin',
|
|
183
|
+
* useFetch: true,
|
|
184
|
+
* });
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* // 仅使用工具函数
|
|
188
|
+
* const sdk = new NetworkSpeedSDK();
|
|
189
|
+
* const speeds = sdk.getAllResourcesSpeeds();
|
|
190
|
+
*/
|
|
191
|
+
constructor(e) {
|
|
192
|
+
e && (this.tester = new l(e));
|
|
149
193
|
}
|
|
150
194
|
/**
|
|
151
195
|
* 执行网速测试
|
|
196
|
+
* @throws {Error} 如果SDK未配置
|
|
152
197
|
*/
|
|
153
198
|
async test() {
|
|
199
|
+
if (!this.tester)
|
|
200
|
+
throw new Error(
|
|
201
|
+
`SDK未配置,请在构造函数中传入配置选项。
|
|
202
|
+
示例:
|
|
203
|
+
new NetworkSpeedSDK({ internetImageUrl: "https://cdn.example.com/test.jpg" })
|
|
204
|
+
或:
|
|
205
|
+
new NetworkSpeedSDK({ internetUrl: "https://cdn.example.com/test.bin", useFetch: true })`
|
|
206
|
+
);
|
|
154
207
|
return this.tester.test();
|
|
155
208
|
}
|
|
156
209
|
/**
|
|
157
210
|
* 获取所有已加载资源的速度信息
|
|
211
|
+
* @returns 资源速度信息数组
|
|
158
212
|
*/
|
|
159
213
|
getAllResourcesSpeeds() {
|
|
160
|
-
return
|
|
214
|
+
return d();
|
|
161
215
|
}
|
|
162
216
|
/**
|
|
163
217
|
* 监听特定资源的性能数据
|
|
218
|
+
* @param urlPattern URL匹配模式
|
|
219
|
+
* @param callback 回调函数
|
|
220
|
+
* @returns 停止监听的函数
|
|
164
221
|
*/
|
|
165
|
-
observeResource(e,
|
|
166
|
-
return this.tester.
|
|
222
|
+
observeResource(e, r) {
|
|
223
|
+
return this.tester || (this.tester = new l({
|
|
224
|
+
internetImageUrl: ""
|
|
225
|
+
})), this.tester.observeResource(e, r);
|
|
167
226
|
}
|
|
168
227
|
/**
|
|
169
228
|
* 更新配置
|
|
229
|
+
* @param options 新的配置选项
|
|
170
230
|
*/
|
|
171
231
|
updateOptions(e) {
|
|
172
|
-
this.tester
|
|
232
|
+
this.tester = new l(e);
|
|
173
233
|
}
|
|
174
234
|
/**
|
|
175
235
|
* 销毁SDK实例
|
|
176
236
|
*/
|
|
177
237
|
destroy() {
|
|
178
|
-
this.tester.destroy();
|
|
238
|
+
this.tester && (this.tester.destroy(), this.tester = null);
|
|
179
239
|
}
|
|
180
240
|
}
|
|
181
|
-
function
|
|
182
|
-
return new
|
|
241
|
+
function g(t) {
|
|
242
|
+
return new U(t);
|
|
183
243
|
}
|
|
184
244
|
export {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
245
|
+
U as NetworkSpeedSDK,
|
|
246
|
+
h as calcSpeedByResource,
|
|
247
|
+
g as createNetworkSpeedSDK,
|
|
248
|
+
w as evaluateNetworkType,
|
|
249
|
+
d as getAllResourcesSpeeds
|
|
190
250
|
};
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
(function(n,c){typeof exports=="object"&&typeof module<"u"?c(exports):typeof define=="function"&&define.amd?define(["exports"],c):(n=typeof globalThis<"u"?globalThis:n||self,c(n.NetworkSpeedJS={}))})(this,(function(n){"use strict";function c(t){const e=t.responseEnd-t.responseStart;if(e<=0||t.transferSize===0)return null;const
|
|
1
|
+
(function(n,c){typeof exports=="object"&&typeof module<"u"?c(exports):typeof define=="function"&&define.amd?define(["exports"],c):(n=typeof globalThis<"u"?globalThis:n||self,c(n.NetworkSpeedJS={}))})(this,(function(n){"use strict";function c(t){const e=t.responseEnd-t.responseStart;if(e<=0||t.transferSize===0)return null;const r=t.transferSize*8/e/1e3,i=t.transferSize/e;return{name:t.name,speedMbps:Number(r.toFixed(2)),speedKBps:Number(i.toFixed(2)),downloadTime:Number(e.toFixed(2)),transferSize:t.transferSize}}function f(){return performance.getEntriesByType("resource").filter(e=>e instanceof PerformanceResourceTiming&&e.transferSize>0).map(c).filter(e=>e!==null)}function w(t){performance.getEntriesByName(t).forEach(()=>{performance.clearResourceTimings()})}function d(t,e={fast:10,medium:2}){return t>=e.fast?"fast":t>=e.medium?"medium":t>0?"slow":"unknown"}class l{options;observer=null;constructor(e){if("useFetch"in e&&e.useFetch){if(!e.internetUrl)throw new Error("Fetch模式必须提供 internetUrl 参数");if(!e.internetUrl.startsWith("http://")&&!e.internetUrl.startsWith("https://"))throw new Error("internetUrl 必须是完整的HTTP/HTTPS URL");this.options={intranetUrl:e.intranetUrl||"",internetUrl:e.internetUrl,timeout:e.timeout||1e4,autoDetect:e.autoDetect??!0,thresholds:e.thresholds||{fast:10,medium:2},useFetch:!0}}else{const r="internetImageUrl"in e?e.internetImageUrl:"";if(!r)throw new Error("图片模式必须提供 internetImageUrl 参数");if(!r.startsWith("http://")&&!r.startsWith("https://"))throw new Error("internetImageUrl 必须是完整的HTTP/HTTPS URL");this.options={intranetUrl:"intranetImageUrl"in e&&e.intranetImageUrl||"",internetUrl:r,timeout:e.timeout||1e4,autoDetect:e.autoDetect??!0,thresholds:e.thresholds||{fast:10,medium:2},useFetch:!1}}if(this.options.intranetUrl&&!this.options.intranetUrl.startsWith("http://")&&!this.options.intranetUrl.startsWith("https://"))throw new Error("intranetUrl/intranetImageUrl 必须是完整的HTTP/HTTPS URL")}async test(){return this.options.autoDetect?this.testWithAutoDetect():this.testSingleUrl(this.options.internetUrl,!1)}async testWithAutoDetect(){if(this.options.intranetUrl)try{return await this.testSingleUrl(this.options.intranetUrl,!0)}catch{console.log("内网测速失败,切换到外网测速")}return this.testSingleUrl(this.options.internetUrl,!1)}testSingleUrl(e,r){return new Promise((i,o)=>{const s=`${e}?t=${Date.now()}`;w(s);const u=new PerformanceObserver(U=>{for(const h of U.getEntries())if(h.entryType==="resource"&&h.name.includes(e)){const a=c(h);if(a){const T={speedMbps:a.speedMbps,speedKBps:a.speedKBps,networkType:d(a.speedMbps,this.options.thresholds),isIntranet:r,duration:a.downloadTime,transferSize:a.transferSize,resourceUrl:e};u.disconnect(),i(T)}}});u.observe({entryTypes:["resource"]});const p=setTimeout(()=>{u.disconnect(),o(new Error(`测速超时: ${e}`))},this.options.timeout);this.options.useFetch?this.loadWithFetch(s,p,u,o):this.loadWithImage(s,p,u,o)})}loadWithImage(e,r,i,o){const s=new Image;s.onload=()=>{clearTimeout(r)},s.onerror=()=>{clearTimeout(r),i.disconnect(),o(new Error(`图片加载失败: ${e}`))},s.src=e}loadWithFetch(e,r,i,o){fetch(e,{method:"GET",cache:"no-store"}).then(s=>{if(!s.ok)throw new Error(`HTTP error! status: ${s.status}`);return s.blob()}).then(()=>{clearTimeout(r)}).catch(s=>{clearTimeout(r),i.disconnect(),o(new Error(`资源加载失败: ${s.message}`))})}observeResource(e,r){const i=new PerformanceObserver(o=>{for(const s of o.getEntries())s.entryType==="resource"&&s.name.includes(e)&&r(s)});return i.observe({entryTypes:["resource"]}),()=>i.disconnect()}updateOptions(e){this.options={...this.options,...e}}destroy(){this.observer&&(this.observer.disconnect(),this.observer=null)}}class m{tester=null;constructor(e){e&&(this.tester=new l(e))}async test(){if(!this.tester)throw new Error(`SDK未配置,请在构造函数中传入配置选项。
|
|
2
|
+
示例:
|
|
3
|
+
new NetworkSpeedSDK({ internetImageUrl: "https://cdn.example.com/test.jpg" })
|
|
4
|
+
或:
|
|
5
|
+
new NetworkSpeedSDK({ internetUrl: "https://cdn.example.com/test.bin", useFetch: true })`);return this.tester.test()}getAllResourcesSpeeds(){return f()}observeResource(e,r){return this.tester||(this.tester=new l({internetImageUrl:""})),this.tester.observeResource(e,r)}updateOptions(e){this.tester=new l(e)}destroy(){this.tester&&(this.tester.destroy(),this.tester=null)}}function S(t){return new m(t)}n.NetworkSpeedSDK=m,n.calcSpeedByResource=c,n.createNetworkSpeedSDK=S,n.evaluateNetworkType=d,n.getAllResourcesSpeeds=f,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "network-speed-js",
|
|
3
3
|
"private": false,
|
|
4
4
|
"description": "A framework-agnostic network speed testing SDK based on Performance API with intranet/internet auto-detection support",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.6",
|
|
6
6
|
"author": "Sunny-117",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"keywords": [
|