network-speed-js 0.0.2 → 1.0.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.
package/README.md CHANGED
@@ -1,12 +1,816 @@
1
+ # Network Speed SDK
1
2
 
2
- # Speed-measuring-gadget
3
+ <div align="center">
3
4
 
4
- TODO:
5
+ 一个基于 **Performance API** 的现代化网速测试 SDK,支持内外网自动检测、资源监听和完整的 TypeScript 类型支持。
5
6
 
6
- - 封装成npm包, sdk化
7
- - 使用文档
7
+ **框架无关 · 开箱即用 · 准确可靠**
8
8
 
9
+ [![npm version](https://img.shields.io/npm/v/network-speed-js.svg)](https://www.npmjs.com/package/network-speed-js)
10
+ [![License](https://img.shields.io/npm/l/network-speed-js.svg)](https://github.com/Sunny-117/network-speed-js/blob/main/LICENSE)
9
11
 
10
- 一款测网速小工具,同时它具有测试内外网的能力
12
+ </div>
11
13
 
12
- Vue2+Axios+Element
14
+ ---
15
+
16
+ ## 🎯 核心亮点
17
+
18
+ ### 从 Axios 到 Performance API 的技术升级
19
+
20
+ 本项目从 **v0.x(Axios 拦截器方案)** 完全重构为 **v1.0(Performance API 方案)**,实现了测速准确度和功能的质的飞跃。
21
+
22
+ **一句话总结:** 从"JavaScript 层面计时"升级到"浏览器底层性能监控",测速更准确、功能更强大。
23
+
24
+ | 对比项 | 旧方案 (Axios) | 新方案 (Performance API) |
25
+ |--------|---------------|------------------------|
26
+ | 时间测量 | JS 层面(不准确) | 浏览器底层(准确) |
27
+ | 传输大小 | 需手动指定 | 自动获取真实值 |
28
+ | 详细时序 | ❌ 无 | ✅ DNS/TCP/TLS/下载 |
29
+ | 资源监听 | ❌ 不支持 | ✅ 支持所有资源 |
30
+ | 框架依赖 | ❌ 依赖 Axios | ✅ 零依赖 |
31
+
32
+ 📖 详细技术对比请查看 [CHANGELOG.md](./CHANGELOG.md)
33
+
34
+ ---
35
+
36
+ ## ✨ 特性
37
+
38
+ - 🚀 **基于 Performance API** - 使用浏览器原生 API,准确测量真实下载速度
39
+ - 🔄 **内外网自动检测** - 智能切换内网/外网测速资源
40
+ - 📊 **完整的性能数据** - 提供速度、耗时、传输大小等详细信息
41
+ - 🎯 **资源监听** - 实时监听页面资源加载性能
42
+ - 💪 **TypeScript 支持** - 完整的类型定义
43
+ - 🌐 **框架无关** - 可用于 Vue、React、Angular 或原生 JavaScript 项目
44
+ - 📦 **轻量级** - 零依赖,体积小巧
45
+
46
+ ## 📦 安装
47
+
48
+ ```bash
49
+ npm install network-speed-js
50
+ ```
51
+
52
+ ## 🚀 快速开始
53
+
54
+ ### 原生 JavaScript / TypeScript
55
+
56
+ ```typescript
57
+ import { NetworkSpeedSDK } from 'network-speed-js';
58
+
59
+ const sdk = new NetworkSpeedSDK({
60
+ internetUrl: 'https://cdn.example.com/test.bin',
61
+ });
62
+
63
+ const result = await sdk.test();
64
+ console.log(`网速: ${result.speedMbps} Mbps`);
65
+ ```
66
+
67
+ ### Vue 3
68
+
69
+ ```vue
70
+ <template>
71
+ <button @click="testSpeed" :disabled="loading">
72
+ {{ loading ? '测速中...' : '开始测速' }}
73
+ </button>
74
+ <div v-if="result">速度: {{ result.speedMbps }} Mbps</div>
75
+ </template>
76
+
77
+ <script setup>
78
+ import { ref } from 'vue';
79
+ import { NetworkSpeedSDK } from 'network-speed-js';
80
+
81
+ const loading = ref(false);
82
+ const result = ref(null);
83
+
84
+ const testSpeed = async () => {
85
+ loading.value = true;
86
+ const sdk = new NetworkSpeedSDK({
87
+ internetUrl: 'https://cdn.example.com/test.bin',
88
+ });
89
+ result.value = await sdk.test();
90
+ loading.value = false;
91
+ };
92
+ </script>
93
+ ```
94
+
95
+ ### React
96
+
97
+ ```tsx
98
+ import { useState } from 'react';
99
+ import { NetworkSpeedSDK } from 'network-speed-js';
100
+
101
+ function SpeedTest() {
102
+ const [loading, setLoading] = useState(false);
103
+ const [result, setResult] = useState(null);
104
+
105
+ const testSpeed = async () => {
106
+ setLoading(true);
107
+ const sdk = new NetworkSpeedSDK({
108
+ internetUrl: 'https://cdn.example.com/test.bin',
109
+ });
110
+ const data = await sdk.test();
111
+ setResult(data);
112
+ setLoading(false);
113
+ };
114
+
115
+ return (
116
+ <div>
117
+ <button onClick={testSpeed} disabled={loading}>
118
+ {loading ? '测速中...' : '开始测速'}
119
+ </button>
120
+ {result && <div>速度: {result.speedMbps} Mbps</div>}
121
+ </div>
122
+ );
123
+ }
124
+ ```
125
+
126
+ ### Angular
127
+
128
+ ```typescript
129
+ import { Component } from '@angular/core';
130
+ import { NetworkSpeedSDK } from 'network-speed-js';
131
+
132
+ @Component({
133
+ selector: 'app-speed-test',
134
+ template: `
135
+ <button (click)="testSpeed()" [disabled]="loading">
136
+ {{ loading ? '测速中...' : '开始测速' }}
137
+ </button>
138
+ <div *ngIf="result">速度: {{ result.speedMbps }} Mbps</div>
139
+ `
140
+ })
141
+ export class SpeedTestComponent {
142
+ loading = false;
143
+ result: any = null;
144
+
145
+ async testSpeed() {
146
+ this.loading = true;
147
+ const sdk = new NetworkSpeedSDK({
148
+ internetUrl: 'https://cdn.example.com/test.bin',
149
+ });
150
+ this.result = await sdk.test();
151
+ this.loading = false;
152
+ }
153
+ }
154
+ ```
155
+
156
+ ## 📖 API 文档
157
+
158
+ ### NetworkSpeedSDK
159
+
160
+ #### 构造函数
161
+
162
+ ```typescript
163
+ new NetworkSpeedSDK(options?: SpeedTestOptions)
164
+ ```
165
+
166
+ **配置选项:**
167
+
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
+
176
+ #### 方法
177
+
178
+ ##### `test(): Promise<SpeedTestResult>`
179
+
180
+ 执行网速测试
181
+
182
+ ```typescript
183
+ const result = await sdk.test();
184
+ // {
185
+ // speedMbps: 45.23,
186
+ // speedKBps: 5653.75,
187
+ // networkType: 'fast',
188
+ // isIntranet: true,
189
+ // duration: 234.56,
190
+ // transferSize: 1323456,
191
+ // resourceUrl: '...'
192
+ // }
193
+ ```
194
+
195
+ ##### `observeResource(urlPattern: string, callback: Function): () => void`
196
+
197
+ 监听特定资源的性能数据
198
+
199
+ ```typescript
200
+ const stopObserver = sdk.observeResource('/api/', (entry) => {
201
+ console.log('资源加载:', entry);
202
+ });
203
+
204
+ // 停止监听
205
+ stopObserver();
206
+ ```
207
+
208
+ ##### `updateOptions(options: Partial<SpeedTestOptions>): void`
209
+
210
+ 更新配置
211
+
212
+ ```typescript
213
+ sdk.updateOptions({ timeout: 15000 });
214
+ ```
215
+
216
+ ##### `destroy(): void`
217
+
218
+ 销毁 SDK 实例
219
+
220
+ ```typescript
221
+ sdk.destroy();
222
+ ```
223
+
224
+ ### 工具函数
225
+
226
+ ```typescript
227
+ import {
228
+ getAllResourcesSpeeds,
229
+ calcSpeedByResource,
230
+ evaluateNetworkType,
231
+ } from 'network-speed-js';
232
+
233
+ // 获取所有资源速度
234
+ const speeds = getAllResourcesSpeeds();
235
+
236
+ // 计算单个资源速度
237
+ const entry = performance.getEntriesByType('resource')[0];
238
+ const speed = calcSpeedByResource(entry);
239
+
240
+ // 评估网络类型
241
+ const type = evaluateNetworkType(15.5); // 'fast'
242
+ ```
243
+
244
+ ### 类型定义
245
+
246
+ ```typescript
247
+ interface SpeedTestResult {
248
+ speedMbps: number; // 下载速度 (Mbps)
249
+ speedKBps: number; // 下载速度 (KB/s)
250
+ networkType: 'fast' | 'medium' | 'slow' | 'unknown';
251
+ isIntranet: boolean; // 是否为内网
252
+ duration: number; // 测试耗时 (ms)
253
+ transferSize: number; // 传输大小 (bytes)
254
+ resourceUrl: string; // 测试资源URL
255
+ }
256
+
257
+ interface ResourceSpeedInfo {
258
+ name: string; // 资源名称
259
+ speedMbps: number; // 下载速度 (Mbps)
260
+ speedKBps: number; // 下载速度 (KB/s)
261
+ downloadTime: number; // 下载时间 (ms)
262
+ transferSize: number; // 传输大小 (bytes)
263
+ }
264
+ ```
265
+
266
+ ## 💡 使用示例
267
+
268
+ ### 1. 内外网自动检测
269
+
270
+ ```typescript
271
+ const sdk = new NetworkSpeedSDK({
272
+ intranetUrl: 'https://internal-cdn.company.com/test.bin',
273
+ internetUrl: 'https://public-cdn.example.com/test.bin',
274
+ autoDetect: true,
275
+ });
276
+
277
+ const result = await sdk.test();
278
+ console.log(result.isIntranet ? '内网环境' : '外网环境');
279
+ ```
280
+
281
+ ### 2. 首屏加载质量评估
282
+
283
+ ```typescript
284
+ const result = await sdk.test();
285
+
286
+ if (result.networkType === 'slow') {
287
+ // 降低图片质量
288
+ // 禁用动画
289
+ // 延迟加载非关键资源
290
+ }
291
+ ```
292
+
293
+ ### 3. 动态 CDN 选择
294
+
295
+ ```typescript
296
+ const result = await sdk.test();
297
+ const cdnUrl = result.isIntranet
298
+ ? 'https://internal-cdn.com'
299
+ : 'https://external-cdn.com';
300
+ ```
301
+
302
+ ### 4. 视频清晰度自适应
303
+
304
+ ```typescript
305
+ const result = await sdk.test();
306
+ const quality = result.speedMbps > 10 ? '1080p' :
307
+ result.speedMbps > 5 ? '720p' : '480p';
308
+ videoPlayer.setQuality(quality);
309
+ ```
310
+
311
+ ### 5. 监听 API 请求性能
312
+
313
+ ```typescript
314
+ const stopObserver = sdk.observeResource('/api/', (entry) => {
315
+ const downloadTime = entry.responseEnd - entry.responseStart;
316
+ const speed = (entry.transferSize * 8) / downloadTime / 1000;
317
+ console.log(`API 速度: ${speed.toFixed(2)} Mbps`);
318
+ });
319
+ ```
320
+
321
+ ### 6. CDN 智能选择
322
+
323
+ ```typescript
324
+ class CDNSelector {
325
+ async selectBestCDN() {
326
+ const cdns = [
327
+ 'https://cdn-a.example.com/test.bin',
328
+ 'https://cdn-b.example.com/test.bin',
329
+ 'https://cdn-c.example.com/test.bin',
330
+ ];
331
+
332
+ const results = await Promise.all(
333
+ cdns.map(async (url) => {
334
+ const sdk = new NetworkSpeedSDK({ internetUrl: url });
335
+ try {
336
+ const result = await sdk.test();
337
+ return { url, speed: result.speedMbps };
338
+ } catch {
339
+ return { url, speed: 0 };
340
+ }
341
+ })
342
+ );
343
+
344
+ return results.reduce((best, curr) =>
345
+ curr.speed > best.speed ? curr : best
346
+ );
347
+ }
348
+ }
349
+ ```
350
+
351
+ ### 7. 网速监控和告警
352
+
353
+ ```typescript
354
+ class NetworkMonitor {
355
+ private sdk: NetworkSpeedSDK;
356
+ private threshold = 2; // Mbps
357
+
358
+ constructor() {
359
+ this.sdk = new NetworkSpeedSDK({
360
+ internetUrl: 'https://cdn.example.com/test.bin',
361
+ });
362
+ }
363
+
364
+ async monitor() {
365
+ const result = await this.sdk.test();
366
+
367
+ if (result.speedMbps < this.threshold) {
368
+ this.alert('网速过慢', result);
369
+ }
370
+
371
+ return result;
372
+ }
373
+
374
+ private alert(message: string, result: SpeedTestResult) {
375
+ console.warn(message, result);
376
+ // 发送告警通知、记录日志、降级处理
377
+ }
378
+ }
379
+
380
+ const monitor = new NetworkMonitor();
381
+ setInterval(() => monitor.monitor(), 60000); // 每分钟检测
382
+ ```
383
+
384
+ ## 🎨 框架集成
385
+
386
+ ### Vue 3 自定义 Hook
387
+
388
+ ```typescript
389
+ // useNetworkSpeed.ts
390
+ import { ref, onUnmounted } from 'vue';
391
+ import { NetworkSpeedSDK } from 'network-speed-js';
392
+
393
+ export function useNetworkSpeed(options = {}) {
394
+ const isLoading = ref(false);
395
+ const result = ref(null);
396
+ const error = ref(null);
397
+ const sdk = new NetworkSpeedSDK(options);
398
+
399
+ const test = async () => {
400
+ isLoading.value = true;
401
+ error.value = null;
402
+ try {
403
+ result.value = await sdk.test();
404
+ } catch (err) {
405
+ error.value = err;
406
+ } finally {
407
+ isLoading.value = false;
408
+ }
409
+ };
410
+
411
+ onUnmounted(() => sdk.destroy());
412
+
413
+ return { isLoading, result, error, test };
414
+ }
415
+ ```
416
+
417
+ 使用:
418
+
419
+ ```vue
420
+ <script setup>
421
+ import { useNetworkSpeed } from './useNetworkSpeed';
422
+
423
+ const { isLoading, result, error, test } = useNetworkSpeed({
424
+ internetUrl: 'https://cdn.example.com/test.bin',
425
+ });
426
+ </script>
427
+ ```
428
+
429
+ ### React 自定义 Hook
430
+
431
+ ```typescript
432
+ // useNetworkSpeed.ts
433
+ import { useState, useEffect, useCallback } from 'react';
434
+ import { NetworkSpeedSDK } from 'network-speed-js';
435
+
436
+ export function useNetworkSpeed(options = {}) {
437
+ const [isLoading, setIsLoading] = useState(false);
438
+ const [result, setResult] = useState(null);
439
+ const [error, setError] = useState(null);
440
+ const [sdk] = useState(() => new NetworkSpeedSDK(options));
441
+
442
+ const test = useCallback(async () => {
443
+ setIsLoading(true);
444
+ setError(null);
445
+ try {
446
+ const testResult = await sdk.test();
447
+ setResult(testResult);
448
+ } catch (err) {
449
+ setError(err);
450
+ } finally {
451
+ setIsLoading(false);
452
+ }
453
+ }, [sdk]);
454
+
455
+ useEffect(() => () => sdk.destroy(), [sdk]);
456
+
457
+ return { isLoading, result, error, test };
458
+ }
459
+ ```
460
+
461
+ ## ⚙️ 配置指南
462
+
463
+ ### 测速资源准备
464
+
465
+ #### 服务端配置(Nginx)
466
+
467
+ ```nginx
468
+ location /speed-test.bin {
469
+ # 禁用缓存
470
+ add_header Cache-Control "no-store, no-cache, must-revalidate";
471
+ add_header Pragma "no-cache";
472
+ add_header Expires "0";
473
+
474
+ # 设置内容类型
475
+ add_header Content-Type "application/octet-stream";
476
+
477
+ # 启用 CORS
478
+ add_header Access-Control-Allow-Origin "*";
479
+ add_header Access-Control-Allow-Methods "GET, OPTIONS";
480
+ }
481
+ ```
482
+
483
+ #### 生成测速文件
484
+
485
+ ```bash
486
+ # 生成 500KB 的随机文件
487
+ dd if=/dev/urandom of=speed-test.bin bs=1024 count=500
488
+ ```
489
+
490
+ **测速文件建议:**
491
+ - 文件大小:200KB ~ 1MB
492
+ - 禁用缓存
493
+ - 启用 CORS
494
+ - 使用 CDN 分发
495
+
496
+ ### 配置项详解
497
+
498
+ #### intranetUrl
499
+
500
+ 内网测速资源URL。如果配置了此项且 `autoDetect` 为 true,会优先尝试内网测速。
501
+
502
+ **建议:**
503
+ - 使用公司内部 CDN 资源
504
+ - 文件大小 200KB ~ 1MB
505
+ - 确保资源稳定可访问
506
+
507
+ #### internetUrl
508
+
509
+ 外网测速资源URL。必填项。
510
+
511
+ **建议:**
512
+ - 使用公共 CDN 资源
513
+ - 文件大小 200KB ~ 1MB
514
+ - 选择地理位置接近用户的 CDN
515
+
516
+ #### timeout
517
+
518
+ 单次测速的超时时间(毫秒)。
519
+
520
+ **建议值:**
521
+ - 快速网络:5000ms
522
+ - 一般网络:10000ms
523
+ - 慢速网络:15000ms
524
+
525
+ #### thresholds
526
+
527
+ 网速评估阈值,用于判断网络类型。
528
+
529
+ ```typescript
530
+ {
531
+ fast: 10, // >= 10 Mbps 为 'fast'
532
+ medium: 2, // >= 2 Mbps 为 'medium'
533
+ // < 2 Mbps 为 'slow'
534
+ }
535
+ ```
536
+
537
+ ## 🔍 核心概念
538
+
539
+ ### Performance API 是什么?
540
+
541
+ Performance API 是浏览器提供的原生性能监控接口,可以精确测量资源加载的各个阶段耗时。
542
+
543
+ ### 测速原理
544
+
545
+ ```
546
+ 已知资源大小 ÷ 实际下载时间 = 当前有效下载速率
547
+ ```
548
+
549
+ 关键时间点:
550
+ - `responseStart`: 开始接收首字节(TTFB 结束)
551
+ - `responseEnd`: 资源下载完成
552
+ - `transferSize`: 实际网络传输字节数(含 header)
553
+
554
+ ### 内外网检测原理
555
+
556
+ 1. 先尝试请求内网资源
557
+ 2. 如果内网资源超时或失败,自动切换到外网资源
558
+ 3. 根据成功的资源判断当前网络环境
559
+
560
+ ## 📊 Performance API vs 其他方案
561
+
562
+ | 方案 | 准确度 | 可控性 | 复杂度 | 推荐 |
563
+ |------|--------|--------|--------|------|
564
+ | Performance API | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | ✅ 主力 |
565
+ | Axios 拦截器 | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | 旧方案 |
566
+ | Network Info API | ⭐⭐ | ⭐ | ⭐ | 辅助 |
567
+ | WebRTC 测速 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | 重量级 |
568
+
569
+ ### 为什么从 Axios 升级到 Performance API?
570
+
571
+ **旧方案(Axios 拦截器)的局限:**
572
+
573
+ ```typescript
574
+ // ❌ 旧方案问题
575
+ const start = Date.now();
576
+ await axios.get(url);
577
+ const end = Date.now();
578
+ const time = end - start; // 包含 JS 执行时间,不准确
579
+ ```
580
+
581
+ - ❌ 只能测量 JavaScript 层面时间
582
+ - ❌ 无法获取真实网络传输大小
583
+ - ❌ 受浏览器缓存影响
584
+ - ❌ 需要手动指定文件大小
585
+
586
+ **新方案(Performance API)的优势:**
587
+
588
+ ```typescript
589
+ // ✅ 新方案优势
590
+ const entry = performance.getEntriesByType('resource')[0];
591
+ const time = entry.responseEnd - entry.responseStart; // 纯网络时间
592
+ const size = entry.transferSize; // 真实传输字节数
593
+ ```
594
+
595
+ - ✅ 浏览器底层 API,准确可靠
596
+ - ✅ 自动获取真实传输大小
597
+ - ✅ 可识别缓存命中
598
+ - ✅ 提供完整的加载时序
599
+
600
+ ## ❓ 常见问题
601
+
602
+ ### Q1: 测速结果不准确?
603
+
604
+ **可能原因:**
605
+ 1. 测速文件太小(< 100KB)
606
+ 2. 测速文件被缓存
607
+ 3. CDN 未命中,回源慢
608
+
609
+ **解决方案:**
610
+ ```typescript
611
+ const sdk = new NetworkSpeedSDK({
612
+ internetUrl: 'https://cdn.example.com/test-500kb.bin',
613
+ timeout: 15000, // 增加超时时间
614
+ });
615
+ ```
616
+
617
+ ### Q2: 如何区分内外网?
618
+
619
+ ```typescript
620
+ const sdk = new NetworkSpeedSDK({
621
+ intranetUrl: 'https://internal.company.com/test.bin', // 只有内网能访问
622
+ internetUrl: 'https://public-cdn.com/test.bin', // 公网可访问
623
+ autoDetect: true,
624
+ });
625
+
626
+ const result = await sdk.test();
627
+ console.log(result.isIntranet ? '内网' : '外网');
628
+ ```
629
+
630
+ ### Q3: 外网 CDN 超时怎么办?
631
+
632
+ ```typescript
633
+ // 方案1: 增加超时时间
634
+ const sdk = new NetworkSpeedSDK({
635
+ internetUrl: 'https://cdn.example.com/test.bin',
636
+ timeout: 20000, // 20秒
637
+ });
638
+
639
+ // 方案2: 使用多个 CDN 备份
640
+ async function testWithFallback() {
641
+ const cdns = [
642
+ 'https://cdn1.example.com/test.bin',
643
+ 'https://cdn2.example.com/test.bin',
644
+ ];
645
+
646
+ for (const url of cdns) {
647
+ try {
648
+ const sdk = new NetworkSpeedSDK({ internetUrl: url });
649
+ return await sdk.test();
650
+ } catch (error) {
651
+ console.warn(`CDN ${url} 失败,尝试下一个`);
652
+ }
653
+ }
654
+ throw new Error('所有 CDN 都不可用');
655
+ }
656
+ ```
657
+
658
+ ### Q4: 移动端支持吗?
659
+
660
+ 完全支持。Performance API 在现代移动浏览器中都可用。
661
+
662
+ **注意事项:**
663
+ - 移动网络波动大,建议多次测试取平均值
664
+ - 注意流量消耗
665
+ - 考虑 WiFi 和移动网络的切换
666
+
667
+ ```typescript
668
+ // 检测网络类型
669
+ const connection = navigator.connection;
670
+ if (connection) {
671
+ console.log('网络类型:', connection.effectiveType);
672
+ console.log('下行速度估算:', connection.downlink, 'Mbps');
673
+ }
674
+ ```
675
+
676
+ ## 🚀 性能优化
677
+
678
+ ### 1. 避免频繁测速
679
+
680
+ ```typescript
681
+ // ❌ 不好的做法
682
+ setInterval(() => sdk.test(), 1000); // 每秒测速,浪费带宽
683
+
684
+ // ✅ 好的做法
685
+ let lastTestTime = 0;
686
+ const MIN_INTERVAL = 60000; // 最小间隔 1 分钟
687
+
688
+ async function testIfNeeded() {
689
+ const now = Date.now();
690
+ if (now - lastTestTime < MIN_INTERVAL) return;
691
+
692
+ lastTestTime = now;
693
+ await sdk.test();
694
+ }
695
+ ```
696
+
697
+ ### 2. 缓存测速结果
698
+
699
+ ```typescript
700
+ class SpeedCache {
701
+ private cache = null;
702
+ private cacheTime = 0;
703
+ private cacheDuration = 5 * 60 * 1000; // 5 分钟
704
+
705
+ async getSpeed(sdk) {
706
+ const now = Date.now();
707
+ if (this.cache && now - this.cacheTime < this.cacheDuration) {
708
+ return this.cache;
709
+ }
710
+
711
+ this.cache = await sdk.test();
712
+ this.cacheTime = now;
713
+ return this.cache;
714
+ }
715
+ }
716
+ ```
717
+
718
+ ### 3. 错误处理
719
+
720
+ ```typescript
721
+ async function testSpeed() {
722
+ const sdk = new NetworkSpeedSDK({
723
+ internetUrl: 'https://cdn.example.com/test.bin',
724
+ timeout: 10000,
725
+ });
726
+
727
+ try {
728
+ const result = await sdk.test();
729
+ return result;
730
+ } catch (error) {
731
+ if (error.message.includes('超时')) {
732
+ console.warn('测速超时,网络可能较慢');
733
+ } else if (error.message.includes('加载失败')) {
734
+ console.error('测速资源不可用');
735
+ }
736
+ throw error;
737
+ }
738
+ }
739
+ ```
740
+
741
+ ## ⚠️ 注意事项
742
+
743
+ ### Performance API 能做什么
744
+
745
+ ✅ 基于真实资源加载评估下载速度
746
+ ✅ 获取详细的资源加载时序
747
+ ✅ 监听页面所有资源性能
748
+ ✅ 支持自定义测速资源
749
+
750
+ ### Performance API 不能做什么
751
+
752
+ ❌ 测量上行速度(upload)
753
+ ❌ 测量丢包率
754
+ ❌ 测量 RTT 抖动
755
+ ❌ 脱离真实请求独立测速
756
+
757
+ ## 🤝 贡献
758
+
759
+ 欢迎提交 Issue 和 Pull Request!
760
+
761
+ ### 本地开发
762
+
763
+ ```bash
764
+ # 克隆项目
765
+ git clone https://github.com/Sunny-117/network-speed-js.git
766
+ cd network-speed-js
767
+
768
+ # 安装依赖
769
+ npm install
770
+
771
+ # 启动开发服务器
772
+ npm run dev
773
+
774
+ # 构建
775
+ npm run build
776
+ ```
777
+
778
+ ### 项目结构
779
+
780
+ ```
781
+ network-speed-js/
782
+ ├── src/
783
+ │ ├── core/ # 核心功能
784
+ │ │ ├── speed-tester.ts
785
+ │ │ └── performance-utils.ts
786
+ │ ├── components/ # Vue 组件(可选)
787
+ │ ├── types.ts # TypeScript 类型定义
788
+ │ ├── sdk.ts # SDK 主入口
789
+ │ ├── index.ts # 导出入口
790
+ │ ├── App.vue # Demo 示例
791
+ │ └── main.ts # 应用入口
792
+ ├── dist/ # 构建输出
793
+ ├── CHANGELOG.md # 更新日志
794
+ └── README.md # 项目文档
795
+ ```
796
+
797
+ ## 📄 License
798
+
799
+ MIT License
800
+
801
+ ## 🔗 相关链接
802
+
803
+ - [更新日志 (CHANGELOG)](./CHANGELOG.md)
804
+ - [Performance API 文档](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API)
805
+ - [PerformanceResourceTiming](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming)
806
+ - [Network Information API](https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API)
807
+
808
+ ---
809
+
810
+ <div align="center">
811
+
812
+ Made with ❤️ by [Sunny-117](https://github.com/Sunny-117)
813
+
814
+ 如果这个项目对你有帮助,请给个 ⭐️ Star 支持一下!
815
+
816
+ </div>