@net-vert/core 0.5.1 → 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,21 +1,22 @@
1
-
2
- ---
3
-
4
1
  # @net-vert/core
5
2
 
6
3
  **轻量级依赖倒置网络请求库,专为扩展和易用而设计。**
7
4
 
5
+ [![npm version](https://img.shields.io/npm/v/@net-vert/core.svg)](https://www.npmjs.com/package/@net-vert/core)
6
+ [![license](https://img.shields.io/npm/l/@net-vert/core.svg)](https://github.com/yvygyyth/net-vert/blob/main/LICENSE)
7
+
8
8
  GitHub 开源仓库 👉 [https://github.com/yvygyyth/net-vert](https://github.com/yvygyyth/net-vert)
9
9
 
10
10
  ---
11
11
 
12
12
  ## ✨ 核心特性
13
13
 
14
- ✅ 解耦网络层,按需注入 axios、fetch 或自定义请求器
15
- 支持缓存、幂等、重试等扩展
16
- ✅ TypeScript 全类型提示,开发更丝滑
17
- 内置幂等、缓存、重试等扩展
18
- 零配置上手,API 极简
14
+ **依赖倒置设计** - 解耦网络层,按需注入 axios、fetch 或自定义请求器
15
+ **中间件扩展** - 内置缓存、幂等、重试、并发控制、同步模式等强大中间件
16
+ **类型安全** - TypeScript 全类型提示,开发体验丝滑
17
+ **零配置上手** - API 极简,开箱即用
18
+ **灵活组合** - 多种中间件自由组合,满足复杂业务场景
19
+ ✅ **Tree-Shaking** - 支持按需引入,打包体积更小
19
20
 
20
21
  ---
21
22
 
@@ -25,118 +26,1094 @@ GitHub 开源仓库 👉 [https://github.com/yvygyyth/net-vert](https://github.c
25
26
  npm install @net-vert/core
26
27
  ```
27
28
 
29
+ 或者使用其他包管理器:
30
+
31
+ ```bash
32
+ pnpm add @net-vert/core
33
+ # 或
34
+ yarn add @net-vert/core
35
+ ```
36
+
28
37
  ---
29
38
 
30
39
  ## 🚀 快速上手
31
40
 
32
- ### 1️⃣ 注入请求器(以 axios 为例)
41
+ ### 1️⃣ 注入请求器
42
+
43
+ 首先,将你的请求函数注入到 `@net-vert/core`。这个函数接收请求配置,返回一个 Promise:
33
44
 
34
45
  ```typescript
35
- import axios from 'axios';
36
- import { inject, useRequestor } from '@net-vert/core';
46
+ import { inject } from '@net-vert/core'
37
47
 
38
- const instance = axios.create({ baseURL: '/api', timeout: 60000 });
39
- const axiosAdapter = (config) => instance.request(config);
48
+ // 创建一个简单的请求函数
49
+ const myRequestor = (config) => {
50
+ // 返回一个 Promise
51
+ return new Promise((resolve, reject) => {
52
+ // 这里可以是任何异步请求实现
53
+ // 例如:fetch、axios、小程序的 wx.request 等
54
+ fetch(config.url, {
55
+ method: config.method,
56
+ headers: config.headers,
57
+ body: config.data ? JSON.stringify(config.data) : undefined
58
+ })
59
+ .then(res => res.json())
60
+ .then(data => resolve(data))
61
+ .catch(err => reject(err))
62
+ })
63
+ }
40
64
 
41
- inject(axiosAdapter); // 注入 axios 实例
65
+ // 注入到 net-vert
66
+ inject(myRequestor)
42
67
  ```
43
68
 
44
- ---
69
+ > **提示**:你可以注入任何符合请求器签名 `(config) => Promise` 的函数,包括 axios、fetch 或自定义请求实现。
45
70
 
46
71
  ### 2️⃣ 发起请求
47
72
 
73
+ 注入完成后,使用 `useRequestor` 或 `createRequestor` 创建请求器:
74
+
75
+ #### 基础用法
76
+
48
77
  ```typescript
49
- const requestor = useRequestor();
78
+ import { useRequestor } from '@net-vert/core'
79
+
80
+ const requestor = useRequestor()
81
+
82
+ // GET 请求
83
+ requestor.get('/user/info', { params: { id: 1 } }).then(console.log)
50
84
 
51
- requestor.get('/user/info', { params: { id: 1 } }).then(console.log);
52
- requestor.post('/user/create', { name: 'Alice' }).then(console.log);
85
+ // POST 请求
86
+ requestor.post('/user/create', { name: 'Alice' }).then(console.log)
87
+
88
+ // PUT 请求
89
+ requestor.put('/user/update', { id: 1, name: 'Bob' })
90
+
91
+ // DELETE 请求
92
+ requestor.delete('/user/delete', { params: { id: 1 } })
53
93
  ```
54
94
 
55
95
  ---
56
96
 
57
- ## 🛠 扩展能力(requestExtender)
97
+ ## 🛠 中间件系统
98
+
99
+ `@net-vert/core` 的强大之处在于其中间件系统。你可以通过 `createRequestor` 结合各种中间件来扩展请求能力。
100
+
101
+ ### 核心 API:`createRequestor`
58
102
 
59
- ### 缓存请求器(cacheRequestor)
60
103
  ```typescript
61
- interface CacheConfig {
62
- key?: (config: UnifiedConfig) => string
63
- duration?: number | ({ key, config, response }) => number
104
+ import { createRequestor, cache, idempotent } from '@net-vert/core'
105
+
106
+ const requestor = createRequestor({
107
+ extensions: [
108
+ idempotent(), // 防止并发重复请求
109
+ cache({ duration: 5000 }) // 缓存 5 秒
110
+ ]
111
+ })
112
+
113
+ // 使用增强后的请求器
114
+ requestor.get('/api/data')
115
+ ```
116
+
117
+ ---
118
+
119
+ ## 📚 内置中间件
120
+
121
+ ### 1. 缓存中间件 (`cache`)
122
+
123
+ 为请求结果添加缓存能力,避免重复请求相同数据。
124
+
125
+ #### 基础用法
126
+
127
+ ```typescript
128
+ import { createRequestor, cache } from '@net-vert/core'
129
+
130
+ const requestor = createRequestor({
131
+ extensions: [
132
+ cache({
133
+ duration: 5000 // 缓存 5 秒
134
+ })
135
+ ]
136
+ })
137
+
138
+ // 首次请求会发起网络请求
139
+ await requestor.get('/api/users')
140
+
141
+ // 5 秒内的相同请求会直接返回缓存
142
+ await requestor.get('/api/users') // 使用缓存
143
+ ```
144
+
145
+ #### 配置选项
146
+
147
+ ```typescript
148
+ interface CacheOptions<D = any, R = any> {
149
+ /**
150
+ * 缓存 key 生成函数
151
+ * 默认:基于 method + url + params 生成哈希
152
+ */
153
+ key?: (ctx: { config: RequestConfig<D> }) => string
154
+
155
+ /**
156
+ * 缓存有效期(毫秒)
157
+ * - number: 固定时长
158
+ * - function: 动态计算(可根据响应内容决定缓存时长)
159
+ */
160
+ duration?: number | ((ctx: {
161
+ key: string
162
+ config: RequestConfig<D>
163
+ response: R
164
+ }) => number)
165
+
166
+ /**
167
+ * 是否持久化到 IndexedDB 或 localStorage
168
+ * 默认:false(仅内存缓存)
169
+ */
64
170
  persist?: boolean
65
- sync?: boolean
66
- isValid?: ({ key, config, cachedData }) => boolean | Promise<boolean>
171
+
172
+ /**
173
+ * 缓存有效性校验函数
174
+ * 返回 false 则忽略缓存,重新请求
175
+ */
176
+ isValid?: (ctx: {
177
+ key: string
178
+ config: RequestConfig<D>
179
+ cachedData?: ExpirableValue<R>
180
+ }) => boolean | Promise<boolean>
67
181
  }
68
182
  ```
69
- - **key**:缓存键生成函数(默认使用URL+参数哈希)
70
- - **duration**:缓存时长(ms),支持动态计算过期时间
71
- - **persist**:是否持久化到indexdeeb, localStorage(同步模式只能存储到localStorage)
72
- - **sync**:是否启用同步模式
73
- - **isValid**:缓存有效性校验函数(同步模式只能传入同步校验函数)
74
183
 
75
- ### 幂等请求器(idempotencyRequestor)
184
+ #### 高级示例
185
+
186
+ **自定义缓存 key**
187
+
188
+ ```typescript
189
+ const requestor = createRequestor({
190
+ extensions: [
191
+ cache({
192
+ duration: 5000,
193
+ // 只根据 URL 生成 key,忽略参数差异
194
+ key: ({ config }) => `custom_${config.url}`
195
+ })
196
+ ]
197
+ })
198
+
199
+ // 这两个请求会共享缓存(因为 URL 相同)
200
+ await requestor.get('/api/users', { params: { id: 1 } })
201
+ await requestor.get('/api/users', { params: { id: 2 } }) // 使用缓存
202
+ ```
203
+
204
+ **动态缓存时长**
205
+
76
206
  ```typescript
77
- (genKey?: (config) => string)
207
+ const requestor = createRequestor({
208
+ extensions: [
209
+ cache({
210
+ // 根据响应内容决定缓存时长
211
+ duration: ({ response }) => {
212
+ // 如果数据标记为"静态",缓存 1 小时
213
+ if (response.isStatic) {
214
+ return 60 * 60 * 1000
215
+ }
216
+ // 否则缓存 5 秒
217
+ return 5000
218
+ }
219
+ })
220
+ ]
221
+ })
78
222
  ```
79
- - **genKey**:自定义请求唯一标识生成函数(默认使用method+url+参数哈希)
80
223
 
81
- ### 同步请求器(syncRequestor)
224
+ **自定义缓存有效性校验**
225
+
82
226
  ```typescript
83
- {
84
- persist?: false
85
- sync?: true
86
- }
227
+ let userLoggedOut = false
228
+
229
+ const requestor = createRequestor({
230
+ extensions: [
231
+ cache({
232
+ duration: 10000,
233
+ // 用户登出后使所有缓存失效
234
+ isValid: ({ cachedData }) => {
235
+ if (userLoggedOut) return false
236
+ return true
237
+ }
238
+ })
239
+ ]
240
+ })
241
+ ```
242
+
243
+ **持久化缓存**
244
+
245
+ ```typescript
246
+ const requestor = createRequestor({
247
+ extensions: [
248
+ cache({
249
+ duration: 24 * 60 * 60 * 1000, // 缓存 24 小时
250
+ persist: true // 持久化到 IndexedDB/localStorage
251
+ })
252
+ ]
253
+ })
87
254
  ```
88
- 入参与 cacheRequestor 一致,不能采用异步方案,第一次请求自动报错,之后再次请求均可同步获取
89
255
 
90
- ### 并发池请求器(concurrentPoolRequestor)
256
+ #### 手动操作缓存
257
+
258
+ 缓存中间件暴露了 `storage` 属性,允许你手动操作缓存:
259
+
91
260
  ```typescript
92
- {
93
- parallelCount?: number;
94
- createId?: (config: UnifiedConfig) => string
261
+ const cacheMiddleware = cache({ duration: 5000 })
262
+
263
+ const requestor = createRequestor({
264
+ extensions: [cacheMiddleware]
265
+ })
266
+
267
+ // 清空所有缓存
268
+ cacheMiddleware.storage.clear()
269
+
270
+ // 获取缓存项
271
+ const cached = cacheMiddleware.storage.getItem('/api/users')
272
+
273
+ // 删除特定缓存
274
+ cacheMiddleware.storage.removeItem('/api/users')
275
+
276
+ // 手动设置缓存
277
+ cacheMiddleware.storage.set('/api/users', { data: [...] })
278
+ ```
279
+
280
+ ---
281
+
282
+ ### 2. 幂等中间件 (`idempotent`)
283
+
284
+ 防止相同的请求并发执行,确保在前一个请求完成前,后续相同请求返回同一个 Promise。
285
+
286
+ #### 基础用法
287
+
288
+ ```typescript
289
+ import { createRequestor, idempotent } from '@net-vert/core'
290
+
291
+ const requestor = createRequestor({
292
+ extensions: [idempotent()]
293
+ })
294
+
295
+ // 并发发起两个相同请求
296
+ const promise1 = requestor.get('/api/users')
297
+ const promise2 = requestor.get('/api/users')
298
+
299
+ // promise1 和 promise2 是同一个 Promise 实例
300
+ console.log(promise1 === promise2) // true
301
+
302
+ // 只会发起一次网络请求
303
+ const [result1, result2] = await Promise.all([promise1, promise2])
304
+ ```
305
+
306
+ #### 配置选项
307
+
308
+ ```typescript
309
+ interface IdempotencyOptions<D = any> {
310
+ /**
311
+ * 自定义请求唯一标识生成函数
312
+ * 默认:基于 method + url + params 生成哈希
313
+ */
314
+ genKey?: (config: RequestConfig<D>) => string
95
315
  }
96
316
  ```
97
- - **maxConcurrent**:最大并行请求数量,默认为4
98
- - **createId**:任务唯一标识生成函数(默认使用时间加随机数)
99
- 其他参数继承于重试请求器
100
317
 
101
- ### 重试请求器(retryRequestor)
318
+ #### 高级示例
319
+
320
+ ```typescript
321
+ const requestor = createRequestor({
322
+ extensions: [
323
+ idempotent({
324
+ // 自定义 key 生成逻辑
325
+ genKey: (config) => `${config.method}:${config.url}`
326
+ })
327
+ ]
328
+ })
329
+ ```
330
+
331
+ #### 与缓存组合使用
332
+
333
+ 幂等中间件通常与缓存中间件配合使用,实现"短期防重复 + 长期缓存":
334
+
335
+ ```typescript
336
+ const requestor = createRequestor({
337
+ extensions: [
338
+ idempotent(), // 防止并发重复(短期)
339
+ cache({ duration: 5000 }) // 缓存结果(长期)
340
+ ]
341
+ })
342
+ ```
343
+
344
+ ---
345
+
346
+ ### 3. 重试中间件 (`retry`)
347
+
348
+ 当请求失败时自动重试,支持固定延迟、指数退避等策略。
349
+
350
+ #### 基础用法
351
+
352
+ ```typescript
353
+ import { createRequestor, retry } from '@net-vert/core'
354
+
355
+ const requestor = createRequestor({
356
+ extensions: [
357
+ retry({
358
+ retries: 3, // 最多重试 3 次
359
+ delay: 1000 // 每次重试延迟 1 秒
360
+ })
361
+ ]
362
+ })
363
+
364
+ // 失败后会自动重试最多 3 次
365
+ await requestor.get('/api/unstable-endpoint')
366
+ ```
367
+
368
+ #### 配置选项
369
+
102
370
  ```typescript
103
- {
371
+ interface RetryOptions<D = any> {
372
+ /**
373
+ * 最大重试次数
374
+ * 默认:3
375
+ */
104
376
  retries?: number
105
- delay?: number | (attempt: number) => number
106
- retryCondition?: (error: any) => boolean
377
+
378
+ /**
379
+ * 重试延迟(毫秒)
380
+ * - number: 固定延迟
381
+ * - function: 动态延迟(实现指数退避等策略)
382
+ * 默认:0
383
+ */
384
+ delay?: number | ((ctx: {
385
+ config: RequestConfig<D>
386
+ lastResponse: any
387
+ attempt: number
388
+ }) => number)
389
+
390
+ /**
391
+ * 重试条件判断函数
392
+ * 返回 true 则重试,false 则直接抛出错误
393
+ * 默认:所有错误都重试
394
+ */
395
+ retryCondition?: (ctx: {
396
+ config: RequestConfig<D>
397
+ lastResponse: any
398
+ attempt: number
399
+ }) => boolean
400
+ }
401
+ ```
402
+
403
+ #### 高级示例
404
+
405
+ **指数退避重试**
406
+
407
+ ```typescript
408
+ const requestor = createRequestor({
409
+ extensions: [
410
+ retry({
411
+ retries: 5,
412
+ // 指数退避:第 n 次重试延迟 2^n * 100ms
413
+ delay: ({ attempt }) => Math.pow(2, attempt) * 100
414
+ })
415
+ ]
416
+ })
417
+ ```
418
+
419
+ **条件重试(仅 5xx 错误)**
420
+
421
+ ```typescript
422
+ const requestor = createRequestor({
423
+ extensions: [
424
+ retry({
425
+ retries: 3,
426
+ delay: 1000,
427
+ // 只在服务器错误时重试
428
+ retryCondition: ({ lastResponse }) => {
429
+ const status = lastResponse?.response?.status
430
+ return status >= 500 && status < 600
431
+ }
432
+ })
433
+ ]
434
+ })
435
+ ```
436
+
437
+ ---
438
+
439
+ ### 4. 并发控制中间件 (`concurrent`)
440
+
441
+ 限制同时发起的请求数量,适用于批量请求场景。
442
+
443
+ #### 基础用法
444
+
445
+ ```typescript
446
+ import { createRequestor, concurrent } from '@net-vert/core'
447
+
448
+ const requestor = createRequestor({
449
+ extensions: [
450
+ concurrent({
451
+ parallelCount: 3 // 最多同时 3 个请求
452
+ })
453
+ ]
454
+ })
455
+
456
+ // 发起 10 个请求,但同时只会执行 3 个
457
+ const promises = []
458
+ for (let i = 0; i < 10; i++) {
459
+ promises.push(requestor.get(`/api/data/${i}`))
460
+ }
461
+ await Promise.all(promises)
462
+ ```
463
+
464
+ #### 配置选项
465
+
466
+ ```typescript
467
+ interface ConcurrentOptions<D = any> {
468
+ /**
469
+ * 最大并行请求数
470
+ * 默认:4
471
+ */
472
+ parallelCount?: number
473
+
474
+ /**
475
+ * 任务唯一标识生成函数
476
+ * 默认:基于时间戳 + 随机数
477
+ */
478
+ createId?: (config: RequestConfig<D>) => string | number
479
+ }
480
+ ```
481
+
482
+ #### 高级示例
483
+
484
+ ```typescript
485
+ const requestor = createRequestor({
486
+ extensions: [
487
+ concurrent({
488
+ parallelCount: 5,
489
+ // 使用请求 URL 作为任务 ID
490
+ createId: ({ config }) => config.url
491
+ })
492
+ ]
493
+ })
494
+ ```
495
+
496
+ #### 与重试组合使用
497
+
498
+ ```typescript
499
+ const requestor = createRequestor({
500
+ extensions: [
501
+ concurrent({ parallelCount: 3 }), // 限制并发数
502
+ retry({ retries: 2, delay: 500 }) // 失败重试
503
+ ]
504
+ })
505
+
506
+ // 批量请求,每个请求都有重试保护
507
+ const results = await Promise.all(
508
+ urls.map(url => requestor.get(url))
509
+ )
510
+ ```
511
+
512
+ ---
513
+
514
+ ### 5. 同步模式中间件 (`sync`)
515
+
516
+ 让异步请求支持"Suspense 风格"的同步调用,适用于 React Suspense 等场景。
517
+
518
+ > **⚠️ 重要**:`sync` 中间件**必须放在中间件数组的第一位**,否则会导致功能异常。详见 [中间件顺序规则](#️-重要中间件顺序规则)。
519
+
520
+ #### 基础用法(Suspense 模式)
521
+
522
+ ```typescript
523
+ import { createRequestor, sync } from '@net-vert/core'
524
+
525
+ const requestor = createRequestor({
526
+ extensions: [sync()]
527
+ })
528
+
529
+ // 首次调用会抛出 Promise(触发 Suspense)
530
+ try {
531
+ const data = requestor.get('/api/users')
532
+ } catch (promise) {
533
+ await promise // 等待数据加载
534
+ }
535
+
536
+ // 再次调用会同步返回缓存数据
537
+ const data = requestor.get('/api/users') // 同步返回,不再抛出
538
+ console.log(data) // 直接获取数据
539
+ ```
540
+
541
+ #### React Suspense 集成
542
+
543
+ ```tsx
544
+ import { createRequestor, sync } from '@net-vert/core'
545
+
546
+ const requestor = createRequestor({
547
+ extensions: [sync()]
548
+ })
549
+
550
+ function UserProfile({ userId }) {
551
+ // 首次渲染会抛出 Promise,触发 Suspense
552
+ // 数据加载完成后重新渲染,此时同步返回数据
553
+ const user = requestor.get(`/api/users/${userId}`)
554
+
555
+ return <div>{user.name}</div>
556
+ }
557
+
558
+ function App() {
559
+ return (
560
+ <Suspense fallback={<div>Loading...</div>}>
561
+ <UserProfile userId={1} />
562
+ </Suspense>
563
+ )
564
+ }
565
+ ```
566
+
567
+ #### 配置选项
568
+
569
+ ```typescript
570
+ interface SyncOptions<D = any, R = any> {
571
+ /**
572
+ * 缓存 key 生成函数
573
+ * 默认:基于 method + url + params 生成
574
+ */
575
+ key?: (ctx: { config: RequestConfig<D> }) => string
576
+
577
+ /**
578
+ * 是否启用 Suspense 模式(抛出 Promise)
579
+ * - true: 首次调用抛出 Promise(默认)
580
+ * - false: 首次调用返回 Promise
581
+ */
582
+ suspense?: boolean
583
+
584
+ /**
585
+ * 自定义 Promise 包装函数
586
+ * 可用于添加元数据或修改返回结构
587
+ */
588
+ wrapSuspense?: (params: {
589
+ key: string
590
+ config: RequestConfig<D>
591
+ p: Promise<R>
592
+ }) => Promise<R>
593
+
594
+ /**
595
+ * 缓存有效期(毫秒)
596
+ * 默认:永久缓存
597
+ */
598
+ duration?: number
599
+
600
+ /**
601
+ * 是否持久化
602
+ * 默认:false
603
+ */
604
+ persist?: boolean
605
+ }
606
+ ```
607
+
608
+ #### 高级示例
609
+
610
+ **非 Suspense 模式**
611
+
612
+ ```typescript
613
+ const requestor = createRequestor({
614
+ extensions: [
615
+ sync({ suspense: false }) // 关闭 Suspense 模式
616
+ ]
617
+ })
618
+
619
+ // 首次调用返回 Promise
620
+ const promise = requestor.get('/api/users')
621
+ await promise
622
+
623
+ // 再次调用同步返回缓存
624
+ const data = requestor.get('/api/users') // 同步返回
625
+ ```
626
+
627
+ **自定义 Promise 包装**
628
+
629
+ ```typescript
630
+ const requestor = createRequestor({
631
+ extensions: [
632
+ sync({
633
+ wrapSuspense: ({ p, key }) => {
634
+ // 给 Promise 添加元数据
635
+ return p.then(data => ({
636
+ ...data,
637
+ __cacheKey: key,
638
+ __timestamp: Date.now()
639
+ }))
640
+ }
641
+ })
642
+ ]
643
+ })
644
+ ```
645
+
646
+ ---
647
+
648
+ ## 🔗 中间件组合
649
+
650
+ 多个中间件可以自由组合,执行顺序遵循数组顺序:
651
+
652
+ ```typescript
653
+ const requestor = createRequestor({
654
+ extensions: [
655
+ idempotent(), // 1. 防止并发重复
656
+ cache({ duration: 5000 }), // 2. 缓存结果
657
+ retry({ retries: 3, delay: 1000 }), // 3. 失败重试
658
+ concurrent({ parallelCount: 3 }) // 4. 限制并发
659
+ ]
660
+ })
661
+ ```
662
+
663
+ ### ⚠️ 重要:中间件顺序规则
664
+
665
+ 在组合中间件时,需要注意以下**强制性规则**,否则会导致功能异常:
666
+
667
+ 1. **同步模式中间件(`sync`)必须放在第一位**
668
+ ```typescript
669
+ // ✅ 正确
670
+ const requestor = createRequestor({
671
+ extensions: [
672
+ sync(), // 必须第一个
673
+ idempotent(),
674
+ cache({ duration: 5000 })
675
+ ]
676
+ })
677
+
678
+ // ❌ 错误 - 某些情况下 sync 不在第一位会导致同步调用失败,幂等缓存的promise会被sync模块直接改变,导致幂等缓存失效.重试也会因为同步模块抛错误耗尽失败次数,同步模块的同步能力也可能因为部分中间件的异步特性而失效
679
+ const requestor = createRequestor({
680
+ extensions: [
681
+ retry() | idempotent(),
682
+ sync() // 错误位置!
683
+ ]
684
+ })
685
+ ```
686
+
687
+ 2. **自定义中间件如果需要拦截所有请求,必须前置**
688
+
689
+ 自定义中间件的位置决定了它在中间件链中的执行时机:
690
+ - **前置**:可以拦截和修改所有请求(包括被其他中间件处理的请求)
691
+ - **后置**:只能处理未被前面中间件拦截的请求(如缓存命中的请求不会到达后置中间件)
692
+
693
+ ```typescript
694
+ const loggerMiddleware: Middleware = async ({ config, next }) => {
695
+ console.log('Request:', config.url)
696
+ return await next()
697
+ }
698
+
699
+ // ✅ 正确 - logger 在最前面,可以记录所有请求
700
+ const requestor = createRequestor({
701
+ extensions: [
702
+ loggerMiddleware, // 第一个执行
703
+ cache({ duration: 5000 }),
704
+ retry({ retries: 3 })
705
+ ]
706
+ })
707
+
708
+ // ⚠️ 注意 - logger 在 cache 之后,缓存命中的请求不会被记录
709
+ const requestor = createRequestor({
710
+ extensions: [
711
+ cache({ duration: 5000 }),
712
+ loggerMiddleware, // 缓存命中时不会执行
713
+ retry({ retries: 3 })
714
+ ]
715
+ })
716
+ ```
717
+
718
+ 3. **推荐的中间件顺序**(从前到后):
719
+ - `sync()` - 同步模式(如果使用)
720
+ - 自定义拦截中间件(日志、鉴权等)
721
+ - `idempotent()` - 幂等处理
722
+ - `cache()` - 缓存
723
+ - `retry()` - 重试
724
+ - `concurrent()` - 并发控制
725
+
726
+ ### 常见组合模式
727
+
728
+ #### 1. 数据查询场景(幂等 + 缓存)
729
+
730
+ ```typescript
731
+ const requestor = createRequestor({
732
+ extensions: [
733
+ idempotent(), // 防止并发重复请求
734
+ cache({ duration: 5000 }) // 缓存 5 秒
735
+ ]
736
+ })
737
+ ```
738
+
739
+ #### 2. 批量请求场景(并发控制 + 重试)
740
+
741
+ ```typescript
742
+ const requestor = createRequestor({
743
+ extensions: [
744
+ concurrent({ parallelCount: 5 }), // 最多同时 5 个请求
745
+ retry({ retries: 3, delay: 500 }) // 失败重试 3 次
746
+ ]
747
+ })
748
+ ```
749
+
750
+ #### 3. 全能组合(适用于复杂场景)
751
+
752
+ ```typescript
753
+ const requestor = createRequestor({
754
+ extensions: [
755
+ idempotent(), // 1. 防止并发重复
756
+ cache({
757
+ duration: 10000,
758
+ persist: true // 2. 持久化缓存 10 秒
759
+ }),
760
+ retry({
761
+ retries: 3,
762
+ delay: ({ attempt }) => Math.pow(2, attempt) * 100 // 3. 指数退避重试
763
+ }),
764
+ concurrent({ parallelCount: 3 }) // 4. 限制并发数
765
+ ]
766
+ })
767
+ ```
768
+
769
+ ---
770
+
771
+ ## 🎯 便捷组合方法
772
+
773
+ 为常见场景提供了预设组合:
774
+
775
+ ### `createCachedIdempotentRequestor`
776
+
777
+ 创建带缓存和幂等的请求器,适用于数据查询接口。
778
+
779
+ ```typescript
780
+ import { createCachedIdempotentRequestor } from '@net-vert/core'
781
+
782
+ const requestor = createCachedIdempotentRequestor({
783
+ duration: 5000, // 缓存 5 秒
784
+ persist: false, // 不持久化
785
+ // 支持所有 cache 和 idempotent 的配置项
786
+ })
787
+
788
+ // 等价于:
789
+ // createRequestor({
790
+ // extensions: [
791
+ // idempotent(),
792
+ // cache({ duration: 5000, persist: false })
793
+ // ]
794
+ // })
795
+ ```
796
+
797
+ ### `createConcurrentRetryRequestor`
798
+
799
+ 创建带并发控制和重试的请求器,适用于批量请求场景。
800
+
801
+ ```typescript
802
+ import { createConcurrentRetryRequestor } from '@net-vert/core'
803
+
804
+ const requestor = createConcurrentRetryRequestor({
805
+ parallelCount: 5, // 最多 5 个并发
806
+ retries: 3, // 重试 3 次
807
+ delay: 1000 // 每次延迟 1 秒
808
+ })
809
+
810
+ // 等价于:
811
+ // createRequestor({
812
+ // extensions: [
813
+ // concurrent({ parallelCount: 5 }),
814
+ // retry({ retries: 3, delay: 1000 })
815
+ // ]
816
+ // })
817
+ ```
818
+
819
+ ---
820
+
821
+ ## 🔑 多实例管理
822
+
823
+ 支持注入和管理多个请求器实例:
824
+
825
+ ```typescript
826
+ import { inject, createRequestor } from '@net-vert/core'
827
+
828
+ // 注入主实例(默认)
829
+ inject(axiosAdapter)
830
+
831
+ // 注入备用实例
832
+ inject(fetchAdapter, 'backup')
833
+
834
+ // 使用默认实例
835
+ const requestor1 = createRequestor()
836
+
837
+ // 使用备用实例
838
+ const requestor2 = createRequestor({ instanceKey: 'backup' })
839
+ ```
840
+
841
+ ---
842
+
843
+ ## 📘 API 参考
844
+
845
+ ### 核心 API
846
+
847
+ #### `inject(requestor, instanceKey?)`
848
+
849
+ 注入请求器到全局容器。
850
+
851
+ - **requestor**: `(config: RequestConfig) => Promise<any>` - 请求器函数
852
+ - **instanceKey**: `string | symbol` - 实例标识(可选,默认为 `'default'`)
853
+
854
+ #### `useRequestor(instanceKey?)`
855
+
856
+ 获取已注入的请求器。
857
+
858
+ - **instanceKey**: `string | symbol` - 实例标识(可选)
859
+ - **返回**: `Requestor` - 请求器对象
860
+
861
+ #### `createRequestor(config?)`
862
+
863
+ 创建带中间件的请求器。
864
+
865
+ - **config.extensions**: `Middleware[]` - 中间件数组
866
+ - **config.instanceKey**: `string | symbol` - 使用的请求器实例标识
867
+ - **返回**: `Requestor` - 增强后的请求器
868
+
869
+ ### Requestor 接口
870
+
871
+ ```typescript
872
+ interface Requestor {
873
+ request<R = any, D = any>(config: RequestConfig<D>): Promise<R>
874
+ get<R = any, D = any>(url: string, config?: Omit<RequestConfig<D>, 'method' | 'url'>): Promise<R>
875
+ post<R = any, D = any>(url: string, data?: D, config?: Omit<RequestConfig<D>, 'method' | 'url'>): Promise<R>
876
+ put<R = any, D = any>(url: string, data?: D, config?: Omit<RequestConfig<D>, 'method' | 'url'>): Promise<R>
877
+ delete<R = any, D = any>(url: string, config?: Omit<RequestConfig<D>, 'method' | 'url'>): Promise<R>
878
+ }
879
+ ```
880
+
881
+ ---
882
+
883
+ ## 🎨 完整示例
884
+
885
+ ### 示例 1:典型的前端应用配置
886
+
887
+ ```typescript
888
+ import axios from 'axios'
889
+ import { inject, createRequestor, idempotent, cache, retry } from '@net-vert/core'
890
+
891
+ // 1. 创建并注入 axios 实例
892
+ const instance = axios.create({
893
+ baseURL: 'https://api.example.com',
894
+ timeout: 10000
895
+ })
896
+ inject(config => instance.request(config))
897
+
898
+ // 2. 创建数据查询请求器(带缓存和幂等)
899
+ export const queryRequestor = createRequestor({
900
+ extensions: [
901
+ idempotent(),
902
+ cache({
903
+ duration: 30000, // 缓存 30 秒
904
+ persist: true // 持久化
905
+ })
906
+ ]
907
+ })
908
+
909
+ // 3. 创建数据变更请求器(带重试)
910
+ export const mutationRequestor = createRequestor({
911
+ extensions: [
912
+ retry({
913
+ retries: 3,
914
+ delay: ({ attempt }) => Math.pow(2, attempt) * 200,
915
+ retryCondition: ({ lastResponse }) => {
916
+ // 只在网络错误或 5xx 时重试
917
+ const status = lastResponse?.response?.status
918
+ return !status || (status >= 500 && status < 600)
919
+ }
920
+ })
921
+ ]
922
+ })
923
+
924
+ // 4. 使用
925
+ async function fetchUserProfile(userId: number) {
926
+ return queryRequestor.get(`/users/${userId}`)
927
+ }
928
+
929
+ async function updateUserProfile(userId: number, data: any) {
930
+ return mutationRequestor.put(`/users/${userId}`, data)
931
+ }
932
+ ```
933
+
934
+ ### 示例 2:批量文件上传
935
+
936
+ ```typescript
937
+ import { createRequestor, concurrent, retry } from '@net-vert/core'
938
+
939
+ const uploadRequestor = createRequestor({
940
+ extensions: [
941
+ concurrent({ parallelCount: 3 }), // 同时最多 3 个上传
942
+ retry({ retries: 2, delay: 1000 }) // 失败重试 2 次
943
+ ]
944
+ })
945
+
946
+ async function uploadFiles(files: File[]) {
947
+ const tasks = files.map(file => {
948
+ const formData = new FormData()
949
+ formData.append('file', file)
950
+ return uploadRequestor.post('/upload', formData)
951
+ })
952
+
953
+ return Promise.all(tasks)
954
+ }
955
+ ```
956
+
957
+ ### 示例 3:React Suspense 集成
958
+
959
+ ```tsx
960
+ import { inject, createRequestor, sync } from '@net-vert/core'
961
+ import { Suspense } from 'react'
962
+
963
+ // 注入请求器
964
+ inject(config => fetch(config.url).then(res => res.json()))
965
+
966
+ // 创建 Suspense 风格的请求器
967
+ const suspenseRequestor = createRequestor({
968
+ extensions: [sync()]
969
+ })
970
+
971
+ // API 封装
972
+ const api = {
973
+ getUser: (id: number) => suspenseRequestor.get(`/api/users/${id}`),
974
+ getPosts: () => suspenseRequestor.get('/api/posts')
975
+ }
976
+
977
+ // 组件
978
+ function UserProfile({ userId }: { userId: number }) {
979
+ const user = api.getUser(userId) // 首次会触发 Suspense
980
+ return <div>{user.name}</div>
981
+ }
982
+
983
+ function App() {
984
+ return (
985
+ <Suspense fallback={<div>Loading...</div>}>
986
+ <UserProfile userId={1} />
987
+ </Suspense>
988
+ )
107
989
  }
108
990
  ```
109
- - **retries**:最大重试次数(默认3次)
110
- - **delay**:重试延迟时间,支持固定数值或动态计算函数
111
- - **retryCondition**:触发重试的条件判断函数
112
991
 
992
+ ---
993
+
994
+ ## 🔧 高级用法
995
+
996
+ ### 自定义中间件
997
+
998
+ 你可以编写自己的中间件来扩展功能:
113
999
 
114
1000
  ```typescript
115
- import { requestExtender } from '@net-vert/core';
1001
+ import { createRequestor, type Middleware } from '@net-vert/core'
1002
+
1003
+ // 自定义日志中间件
1004
+ const loggerMiddleware: Middleware = async ({ config, next }) => {
1005
+ console.log('Request:', config.method, config.url)
1006
+ const startTime = Date.now()
1007
+
1008
+ try {
1009
+ const result = await next()
1010
+ console.log('Success:', Date.now() - startTime, 'ms')
1011
+ return result
1012
+ } catch (error) {
1013
+ console.error('Error:', error)
1014
+ throw error
1015
+ }
1016
+ }
1017
+
1018
+ // 使用自定义中间件
1019
+ const requestor = createRequestor({
1020
+ extensions: [loggerMiddleware]
1021
+ })
116
1022
  ```
117
1023
 
118
- **缓存请求例子**
1024
+ > **⚠️ 重要提示**:如果你的自定义中间件需要拦截所有请求(如日志记录、鉴权检查等),**必须将其放在中间件数组的最前面**(`sync` 除外)。否则,被前置中间件(如 `cache`)拦截的请求不会经过你的自定义中间件。详见 [中间件顺序规则](#️-重要中间件顺序规则)。
1025
+
1026
+ ### 动态切换请求器
1027
+
119
1028
  ```typescript
120
- const {requestor} = requestExtender.cacheRequestor();
121
- requestor.get('/user/info', { params: { id: 1 } });
1029
+ import { inject, useRequestor } from '@net-vert/core'
1030
+
1031
+ // 注入多个请求器
1032
+ inject(axiosAdapter, 'axios')
1033
+ inject(fetchAdapter, 'fetch')
1034
+
1035
+ // 动态选择
1036
+ function getRequestor(type: 'axios' | 'fetch') {
1037
+ return useRequestor(type)
1038
+ }
1039
+
1040
+ // 使用
1041
+ const requestor = getRequestor('axios')
1042
+ requestor.get('/api/data')
1043
+ ```
1044
+
1045
+ ---
1046
+
1047
+ ## 🧪 测试支持
1048
+
1049
+ 轻松进行单元测试:
1050
+
1051
+ ```typescript
1052
+ import { inject, createRequestor, cache } from '@net-vert/core'
1053
+ import { vi } from 'vitest'
1054
+
1055
+ describe('API Tests', () => {
1056
+ it('should cache requests', async () => {
1057
+ // 创建 mock 请求器
1058
+ const mockRequestor = vi.fn(async (config) => ({
1059
+ code: 200,
1060
+ data: { url: config.url }
1061
+ }))
1062
+
1063
+ inject(mockRequestor)
1064
+
1065
+ const requestor = createRequestor({
1066
+ extensions: [cache({ duration: 5000 })]
1067
+ })
1068
+
1069
+ // 发起两次相同请求
1070
+ await requestor.get('/api/test')
1071
+ await requestor.get('/api/test')
1072
+
1073
+ // 验证只调用了一次
1074
+ expect(mockRequestor).toHaveBeenCalledTimes(1)
1075
+ })
1076
+ })
122
1077
  ```
123
1078
 
1079
+ ---
124
1080
 
125
- ## 📤 开源信息
1081
+ ## 📤 项目信息
126
1082
 
127
- - 仓库地址:[https://github.com/yvygyyth/net-vert](https://github.com/yvygyyth/net-vert)
128
- - 许可证:MIT
129
- - 支持 Tree-Shaking
130
- - 无副作用 (`sideEffects: false`)
1083
+ - **仓库地址**: [https://github.com/yvygyyth/net-vert](https://github.com/yvygyyth/net-vert)
1084
+ - **问题反馈**: [GitHub Issues](https://github.com/yvygyyth/net-vert/issues)
1085
+ - **许可证**: MIT
1086
+ - **作者**: yuzinan <1589937631@qq.com>
131
1087
 
132
1088
  ---
133
1089
 
134
- ## 🔥 设计理念
1090
+ ## 💡 设计理念
1091
+
1092
+ 1. **依赖倒置** - 网络层完全解耦,未来可自由切换底层实现
1093
+ 2. **组合优于继承** - 通过中间件组合实现复杂功能
1094
+ 3. **渐进式增强** - 零配置可用,按需添加能力
1095
+ 4. **类型安全** - 完整的 TypeScript 支持
1096
+ 5. **轻量纯粹** - 核心代码极简,扩展独立管理
1097
+
1098
+ ---
1099
+
1100
+ ## 🤝 贡献
1101
+
1102
+ 欢迎提交 Issue 和 Pull Request!
1103
+
1104
+ ---
1105
+
1106
+ ## 📝 更新日志
1107
+
1108
+ ### v1.0.0 (2024-11)
135
1109
 
136
- - 网络层完全解耦,未来自由扩展
137
- - 内置强大的请求能力,零上手成本
138
- - 存储与缓存拆分,保持核心轻量纯粹
1110
+ - 🎉 正式版本发布
1111
+ - ✨ 完整的中间件系统
1112
+ - ✨ 支持缓存、幂等、重试、并发控制、同步模式
1113
+ - ✨ 完整的 TypeScript 类型支持
1114
+ - ✨ 支持多实例管理
1115
+ - 📚 完善的文档和示例
139
1116
 
140
1117
  ---
141
1118
 
142
- 如果你确定了包名是 `@net-vert/cache`,我可以直接帮你生成一段未来文档里的“缓存插件使用示例”,随时告诉我!
1119
+ 如有任何问题或建议,欢迎在 [GitHub](https://github.com/yvygyyth/net-vert) 上联系我们!