network-speed-js 1.0.5 → 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 +66 -2
- package/dist/core/speed-tester.d.ts.map +1 -1
- package/dist/network-speed-js.js +85 -70
- package/dist/network-speed-js.umd.js +2 -2
- 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,14 +51,22 @@
|
|
|
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
|
|
|
56
72
|
**方式一:使用图片资源测速(推荐,默认模式,无需CORS)**
|
|
@@ -173,8 +189,12 @@ export class SpeedTestComponent {
|
|
|
173
189
|
}
|
|
174
190
|
```
|
|
175
191
|
|
|
192
|
+
<div align="center">
|
|
193
|
+
|
|
176
194
|
## 📖 API 文档
|
|
177
195
|
|
|
196
|
+
</div>
|
|
197
|
+
|
|
178
198
|
### NetworkSpeedSDK
|
|
179
199
|
|
|
180
200
|
#### 构造函数
|
|
@@ -331,8 +351,12 @@ interface ResourceSpeedInfo {
|
|
|
331
351
|
}
|
|
332
352
|
```
|
|
333
353
|
|
|
354
|
+
<div align="center">
|
|
355
|
+
|
|
334
356
|
## 💡 使用示例
|
|
335
357
|
|
|
358
|
+
</div>
|
|
359
|
+
|
|
336
360
|
### 1. 使用图片资源测速(默认,推荐)
|
|
337
361
|
|
|
338
362
|
```typescript
|
|
@@ -479,8 +503,12 @@ const monitor = new NetworkMonitor();
|
|
|
479
503
|
setInterval(() => monitor.monitor(), 60000); // 每分钟检测
|
|
480
504
|
```
|
|
481
505
|
|
|
506
|
+
<div align="center">
|
|
507
|
+
|
|
482
508
|
## 🎨 框架集成
|
|
483
509
|
|
|
510
|
+
</div>
|
|
511
|
+
|
|
484
512
|
### Vue 3 自定义 Hook
|
|
485
513
|
|
|
486
514
|
```typescript
|
|
@@ -556,8 +584,12 @@ export function useNetworkSpeed(options = {}) {
|
|
|
556
584
|
}
|
|
557
585
|
```
|
|
558
586
|
|
|
587
|
+
<div align="center">
|
|
588
|
+
|
|
559
589
|
## ⚙️ 配置指南
|
|
560
590
|
|
|
591
|
+
</div>
|
|
592
|
+
|
|
561
593
|
### 测速资源准备
|
|
562
594
|
|
|
563
595
|
#### 服务端配置(Nginx)
|
|
@@ -641,8 +673,12 @@ dd if=/dev/urandom of=speed-test.bin bs=1024 count=500
|
|
|
641
673
|
}
|
|
642
674
|
```
|
|
643
675
|
|
|
676
|
+
<div align="center">
|
|
677
|
+
|
|
644
678
|
## 🔍 核心概念
|
|
645
679
|
|
|
680
|
+
</div>
|
|
681
|
+
|
|
646
682
|
### Performance API 是什么?
|
|
647
683
|
|
|
648
684
|
Performance API 是浏览器提供的原生性能监控接口,可以精确测量资源加载的各个阶段耗时。
|
|
@@ -664,8 +700,12 @@ Performance API 是浏览器提供的原生性能监控接口,可以精确测
|
|
|
664
700
|
2. 如果内网资源超时或失败,自动切换到外网资源
|
|
665
701
|
3. 根据成功的资源判断当前网络环境
|
|
666
702
|
|
|
703
|
+
<div align="center">
|
|
704
|
+
|
|
667
705
|
## 📊 Performance API vs 其他方案
|
|
668
706
|
|
|
707
|
+
</div>
|
|
708
|
+
|
|
669
709
|
| 方案 | 准确度 | 可控性 | 复杂度 | 推荐 |
|
|
670
710
|
|------|--------|--------|--------|------|
|
|
671
711
|
| Performance API | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | ✅ 主力 |
|
|
@@ -704,8 +744,12 @@ const size = entry.transferSize; // 真实传输字节数
|
|
|
704
744
|
- ✅ 可识别缓存命中
|
|
705
745
|
- ✅ 提供完整的加载时序
|
|
706
746
|
|
|
747
|
+
<div align="center">
|
|
748
|
+
|
|
707
749
|
## ❓ 常见问题
|
|
708
750
|
|
|
751
|
+
</div>
|
|
752
|
+
|
|
709
753
|
### Q1: 测速结果不准确?
|
|
710
754
|
|
|
711
755
|
**可能原因:**
|
|
@@ -780,8 +824,12 @@ if (connection) {
|
|
|
780
824
|
}
|
|
781
825
|
```
|
|
782
826
|
|
|
827
|
+
<div align="center">
|
|
828
|
+
|
|
783
829
|
## 🚀 性能优化
|
|
784
830
|
|
|
831
|
+
</div>
|
|
832
|
+
|
|
785
833
|
### 1. 避免频繁测速
|
|
786
834
|
|
|
787
835
|
```typescript
|
|
@@ -845,8 +893,12 @@ async function testSpeed() {
|
|
|
845
893
|
}
|
|
846
894
|
```
|
|
847
895
|
|
|
896
|
+
<div align="center">
|
|
897
|
+
|
|
848
898
|
## ⚠️ 注意事项
|
|
849
899
|
|
|
900
|
+
</div>
|
|
901
|
+
|
|
850
902
|
### Performance API 能做什么
|
|
851
903
|
|
|
852
904
|
✅ 基于真实资源加载评估下载速度
|
|
@@ -861,8 +913,12 @@ async function testSpeed() {
|
|
|
861
913
|
❌ 测量 RTT 抖动
|
|
862
914
|
❌ 脱离真实请求独立测速
|
|
863
915
|
|
|
916
|
+
<div align="center">
|
|
917
|
+
|
|
864
918
|
## 🤝 贡献
|
|
865
919
|
|
|
920
|
+
</div>
|
|
921
|
+
|
|
866
922
|
欢迎提交 Issue 和 Pull Request!
|
|
867
923
|
|
|
868
924
|
### 本地开发
|
|
@@ -901,12 +957,20 @@ network-speed-js/
|
|
|
901
957
|
└── README.md # 项目文档
|
|
902
958
|
```
|
|
903
959
|
|
|
960
|
+
<div align="center">
|
|
961
|
+
|
|
904
962
|
## 📄 License
|
|
905
963
|
|
|
964
|
+
</div>
|
|
965
|
+
|
|
906
966
|
MIT License
|
|
907
967
|
|
|
968
|
+
<div align="center">
|
|
969
|
+
|
|
908
970
|
## 🔗 相关链接
|
|
909
971
|
|
|
972
|
+
</div>
|
|
973
|
+
|
|
910
974
|
- [更新日志 (CHANGELOG)](./CHANGELOG.md)
|
|
911
975
|
- [Performance API 文档](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API)
|
|
912
976
|
- [PerformanceResourceTiming](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming)
|
|
@@ -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,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;
|
|
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/network-speed-js.js
CHANGED
|
@@ -2,16 +2,16 @@ 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
17
|
).map(h).filter((e) => e !== null);
|
|
@@ -24,25 +24,40 @@ function p(t) {
|
|
|
24
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
30
|
constructor(e) {
|
|
31
|
-
"useFetch" in e && e.useFetch
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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");
|
|
46
61
|
}
|
|
47
62
|
/**
|
|
48
63
|
* 执行测速
|
|
@@ -65,74 +80,74 @@ class a {
|
|
|
65
80
|
/**
|
|
66
81
|
* 测试单个URL
|
|
67
82
|
*/
|
|
68
|
-
testSingleUrl(e,
|
|
69
|
-
return new Promise((n,
|
|
70
|
-
const
|
|
71
|
-
p(
|
|
72
|
-
const c = new PerformanceObserver((
|
|
73
|
-
for (const
|
|
74
|
-
if (
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
77
|
-
const
|
|
78
|
-
speedMbps:
|
|
79
|
-
speedKBps:
|
|
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,
|
|
80
95
|
networkType: w(
|
|
81
|
-
|
|
96
|
+
o.speedMbps,
|
|
82
97
|
this.options.thresholds
|
|
83
98
|
),
|
|
84
|
-
isIntranet:
|
|
85
|
-
duration:
|
|
86
|
-
transferSize:
|
|
99
|
+
isIntranet: r,
|
|
100
|
+
duration: o.downloadTime,
|
|
101
|
+
transferSize: o.transferSize,
|
|
87
102
|
resourceUrl: e
|
|
88
103
|
};
|
|
89
|
-
c.disconnect(), n(
|
|
104
|
+
c.disconnect(), n(m);
|
|
90
105
|
}
|
|
91
106
|
}
|
|
92
107
|
});
|
|
93
108
|
c.observe({ entryTypes: ["resource"] });
|
|
94
|
-
const
|
|
95
|
-
c.disconnect(),
|
|
109
|
+
const u = setTimeout(() => {
|
|
110
|
+
c.disconnect(), i(new Error(`测速超时: ${e}`));
|
|
96
111
|
}, this.options.timeout);
|
|
97
|
-
this.options.useFetch ? this.loadWithFetch(
|
|
112
|
+
this.options.useFetch ? this.loadWithFetch(s, u, c, i) : this.loadWithImage(s, u, c, i);
|
|
98
113
|
});
|
|
99
114
|
}
|
|
100
115
|
/**
|
|
101
116
|
* 使用 Image 对象加载资源(默认方式,不受跨域限制)
|
|
102
117
|
*/
|
|
103
|
-
loadWithImage(e,
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
clearTimeout(
|
|
107
|
-
},
|
|
108
|
-
clearTimeout(
|
|
109
|
-
},
|
|
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;
|
|
110
125
|
}
|
|
111
126
|
/**
|
|
112
127
|
* 使用 fetch API 加载资源(需要 CORS 支持)
|
|
113
128
|
*/
|
|
114
|
-
loadWithFetch(e,
|
|
129
|
+
loadWithFetch(e, r, n, i) {
|
|
115
130
|
fetch(e, {
|
|
116
131
|
method: "GET",
|
|
117
132
|
cache: "no-store"
|
|
118
133
|
// 禁用缓存
|
|
119
|
-
}).then((
|
|
120
|
-
if (!
|
|
121
|
-
throw new Error(`HTTP error! status: ${
|
|
122
|
-
return
|
|
134
|
+
}).then((s) => {
|
|
135
|
+
if (!s.ok)
|
|
136
|
+
throw new Error(`HTTP error! status: ${s.status}`);
|
|
137
|
+
return s.blob();
|
|
123
138
|
}).then(() => {
|
|
124
|
-
clearTimeout(
|
|
125
|
-
}).catch((
|
|
126
|
-
clearTimeout(
|
|
139
|
+
clearTimeout(r);
|
|
140
|
+
}).catch((s) => {
|
|
141
|
+
clearTimeout(r), n.disconnect(), i(new Error(`资源加载失败: ${s.message}`));
|
|
127
142
|
});
|
|
128
143
|
}
|
|
129
144
|
/**
|
|
130
145
|
* 监听特定资源的性能数据
|
|
131
146
|
*/
|
|
132
|
-
observeResource(e,
|
|
133
|
-
const n = new PerformanceObserver((
|
|
134
|
-
for (const
|
|
135
|
-
|
|
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);
|
|
136
151
|
});
|
|
137
152
|
return n.observe({ entryTypes: ["resource"] }), () => n.disconnect();
|
|
138
153
|
}
|
|
@@ -149,7 +164,7 @@ class a {
|
|
|
149
164
|
this.observer && (this.observer.disconnect(), this.observer = null);
|
|
150
165
|
}
|
|
151
166
|
}
|
|
152
|
-
class
|
|
167
|
+
class U {
|
|
153
168
|
tester = null;
|
|
154
169
|
/**
|
|
155
170
|
* 创建SDK实例
|
|
@@ -174,7 +189,7 @@ class S {
|
|
|
174
189
|
* const speeds = sdk.getAllResourcesSpeeds();
|
|
175
190
|
*/
|
|
176
191
|
constructor(e) {
|
|
177
|
-
e && (this.tester = new
|
|
192
|
+
e && (this.tester = new l(e));
|
|
178
193
|
}
|
|
179
194
|
/**
|
|
180
195
|
* 执行网速测试
|
|
@@ -196,7 +211,7 @@ class S {
|
|
|
196
211
|
* @returns 资源速度信息数组
|
|
197
212
|
*/
|
|
198
213
|
getAllResourcesSpeeds() {
|
|
199
|
-
return
|
|
214
|
+
return d();
|
|
200
215
|
}
|
|
201
216
|
/**
|
|
202
217
|
* 监听特定资源的性能数据
|
|
@@ -204,17 +219,17 @@ class S {
|
|
|
204
219
|
* @param callback 回调函数
|
|
205
220
|
* @returns 停止监听的函数
|
|
206
221
|
*/
|
|
207
|
-
observeResource(e,
|
|
208
|
-
return this.tester || (this.tester = new
|
|
222
|
+
observeResource(e, r) {
|
|
223
|
+
return this.tester || (this.tester = new l({
|
|
209
224
|
internetImageUrl: ""
|
|
210
|
-
})), this.tester.observeResource(e,
|
|
225
|
+
})), this.tester.observeResource(e, r);
|
|
211
226
|
}
|
|
212
227
|
/**
|
|
213
228
|
* 更新配置
|
|
214
229
|
* @param options 新的配置选项
|
|
215
230
|
*/
|
|
216
231
|
updateOptions(e) {
|
|
217
|
-
this.tester = new
|
|
232
|
+
this.tester = new l(e);
|
|
218
233
|
}
|
|
219
234
|
/**
|
|
220
235
|
* 销毁SDK实例
|
|
@@ -223,13 +238,13 @@ class S {
|
|
|
223
238
|
this.tester && (this.tester.destroy(), this.tester = null);
|
|
224
239
|
}
|
|
225
240
|
}
|
|
226
|
-
function
|
|
227
|
-
return new
|
|
241
|
+
function g(t) {
|
|
242
|
+
return new U(t);
|
|
228
243
|
}
|
|
229
244
|
export {
|
|
230
|
-
|
|
245
|
+
U as NetworkSpeedSDK,
|
|
231
246
|
h as calcSpeedByResource,
|
|
232
|
-
|
|
247
|
+
g as createNetworkSpeedSDK,
|
|
233
248
|
w as evaluateNetworkType,
|
|
234
|
-
|
|
249
|
+
d as getAllResourcesSpeeds
|
|
235
250
|
};
|
|
@@ -1,5 +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
2
|
示例:
|
|
3
3
|
new NetworkSpeedSDK({ internetImageUrl: "https://cdn.example.com/test.jpg" })
|
|
4
4
|
或:
|
|
5
|
-
new NetworkSpeedSDK({ internetUrl: "https://cdn.example.com/test.bin", useFetch: true })`);return this.tester.test()}getAllResourcesSpeeds(){return
|
|
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": [
|