Dependency Inversion Network Library with Type-Safe Injection.
npm install @net-vert/corebash
npm install @net-vert/core
`
或者使用其他包管理器:
`bash
pnpm add @net-vert/core
或
yarn add @net-vert/core
`
---
🚀 快速上手
$3
首先,将你的请求函数注入到 @net-vert/core。这个函数接收请求配置,返回一个 Promise:
`typescript
import { inject } from '@net-vert/core'
// 创建一个简单的请求函数
const myRequestor = (config) => {
// 返回一个 Promise
return new Promise((resolve, reject) => {
// 这里可以是任何异步请求实现
// 例如:fetch、axios、小程序的 wx.request 等
fetch(config.url, {
method: config.method,
headers: config.headers,
body: config.data ? JSON.stringify(config.data) : undefined
})
.then(res => res.json())
.then(data => resolve(data))
.catch(err => reject(err))
})
}
// 注入到 net-vert
inject(myRequestor)
`
> 提示:你可以注入任何符合请求器签名 (config) => Promise 的函数,包括 axios、fetch 或自定义请求实现。
$3
注入完成后,使用 useRequestor 或 createRequestor 创建请求器:
#### 基础用法
`typescript
import { useRequestor } from '@net-vert/core'
const requestor = useRequestor()
// GET 请求
requestor.get('/user/info', { params: { id: 1 } }).then(console.log)
// POST 请求
requestor.post('/user/create', { name: 'Alice' }).then(console.log)
// PUT 请求
requestor.put('/user/update', { id: 1, name: 'Bob' })
// DELETE 请求
requestor.delete('/user/delete', { params: { id: 1 } })
`
---
🛠 中间件系统
@net-vert/core 的强大之处在于其中间件系统。你可以通过 createRequestor 结合各种中间件来扩展请求能力。
$3
`typescript
import { createRequestor, cache, idempotent } from '@net-vert/core'
const requestor = createRequestor({
extensions: [
idempotent(), // 防止并发重复请求
cache({ duration: 5000 }) // 缓存 5 秒
]
})
// 使用增强后的请求器
requestor.get('/api/data')
`
---
📚 内置中间件
$3
为请求结果添加缓存能力,避免重复请求相同数据。
#### 基础用法
`typescript
import { createRequestor, cache } from '@net-vert/core'
const requestor = createRequestor({
extensions: [
cache({
duration: 5000 // 缓存 5 秒
})
]
})
// 首次请求会发起网络请求
await requestor.get('/api/users')
// 5 秒内的相同请求会直接返回缓存
await requestor.get('/api/users') // 使用缓存
`
#### 配置选项
`typescript
interface CacheOptions {
/**
* 缓存 key 生成函数
* 默认:基于 method + url + params 生成哈希
*/
key?: (ctx: { config: RequestConfig }) => string
/**
* 缓存有效期(毫秒)
* - number: 固定时长
* - function: 动态计算(可根据响应内容决定缓存时长)
*/
duration?: number | ((ctx: {
key: string
config: RequestConfig
response: R
}) => number)
/**
* 是否持久化到 IndexedDB 或 localStorage
* 默认:false(仅内存缓存)
*/
persist?: boolean
/**
* 缓存有效性校验函数
* 返回 false 则忽略缓存,重新请求
*/
isValid?: (ctx: {
key: string
config: RequestConfig
cachedData?: ExpirableValue
}) => boolean | Promise
}
`
#### 高级示例
自定义缓存 key
`typescript
const requestor = createRequestor({
extensions: [
cache({
duration: 5000,
// 只根据 URL 生成 key,忽略参数差异
key: ({ config }) => custom_${config.url}
})
]
})
// 这两个请求会共享缓存(因为 URL 相同)
await requestor.get('/api/users', { params: { id: 1 } })
await requestor.get('/api/users', { params: { id: 2 } }) // 使用缓存
`
动态缓存时长
`typescript
const requestor = createRequestor({
extensions: [
cache({
// 根据响应内容决定缓存时长
duration: ({ response }) => {
// 如果数据标记为"静态",缓存 1 小时
if (response.isStatic) {
return 60 60 1000
}
// 否则缓存 5 秒
return 5000
}
})
]
})
`
自定义缓存有效性校验
`typescript
let userLoggedOut = false
const requestor = createRequestor({
extensions: [
cache({
duration: 10000,
// 用户登出后使所有缓存失效
isValid: ({ cachedData }) => {
if (userLoggedOut) return false
return true
}
})
]
})
`
持久化缓存
`typescript
const requestor = createRequestor({
extensions: [
cache({
duration: 24 60 60 * 1000, // 缓存 24 小时
persist: true // 持久化到 IndexedDB/localStorage
})
]
})
`
#### 手动操作缓存
缓存中间件暴露了 storage 属性,允许你手动操作缓存:
`typescript
const cacheMiddleware = cache({ duration: 5000 })
const requestor = createRequestor({
extensions: [cacheMiddleware]
})
// 清空所有缓存
cacheMiddleware.storage.clear()
// 获取缓存项
const cached = cacheMiddleware.storage.getItem('/api/users')
// 删除特定缓存
cacheMiddleware.storage.removeItem('/api/users')
// 手动设置缓存
cacheMiddleware.storage.set('/api/users', { data: [...] })
`
---
$3
防止相同的请求并发执行,确保在前一个请求完成前,后续相同请求返回同一个 Promise。
#### 基础用法
`typescript
import { createRequestor, idempotent } from '@net-vert/core'
const requestor = createRequestor({
extensions: [idempotent()]
})
// 并发发起两个相同请求
const promise1 = requestor.get('/api/users')
const promise2 = requestor.get('/api/users')
// promise1 和 promise2 是同一个 Promise 实例
console.log(promise1 === promise2) // true
// 只会发起一次网络请求
const [result1, result2] = await Promise.all([promise1, promise2])
`
#### 配置选项
`typescript
interface IdempotencyOptions {
/**
* 自定义请求唯一标识生成函数
* 默认:基于 method + url + params 生成哈希
*/
genKey?: (config: RequestConfig) => string
}
`
#### 高级示例
`typescript
const requestor = createRequestor({
extensions: [
idempotent({
// 自定义 key 生成逻辑
genKey: (config) => ${config.method}:${config.url}
})
]
})
`
#### 与缓存组合使用
幂等中间件通常与缓存中间件配合使用,实现"短期防重复 + 长期缓存":
`typescript
const requestor = createRequestor({
extensions: [
idempotent(), // 防止并发重复(短期)
cache({ duration: 5000 }) // 缓存结果(长期)
]
})
`
---
$3
当请求失败时自动重试,支持固定延迟、指数退避等策略。
#### 基础用法
`typescript
import { createRequestor, retry } from '@net-vert/core'
const requestor = createRequestor({
extensions: [
retry({
retries: 3, // 最多重试 3 次
delay: 1000 // 每次重试延迟 1 秒
})
]
})
// 失败后会自动重试最多 3 次
await requestor.get('/api/unstable-endpoint')
`
#### 配置选项
`typescript
interface RetryOptions {
/**
* 最大重试次数
* 默认:3
*/
retries?: number
/**
* 重试延迟(毫秒)
* - number: 固定延迟
* - function: 动态延迟(实现指数退避等策略)
* 默认:0
*/
delay?: number | ((ctx: {
config: RequestConfig
lastResponse: any
attempt: number
}) => number)
/**
* 重试条件判断函数
* 返回 true 则重试,false 则直接抛出错误
* 默认:所有错误都重试
*/
retryCondition?: (ctx: {
config: RequestConfig
lastResponse: any
attempt: number
}) => boolean
}
`
#### 高级示例
指数退避重试
`typescript
const requestor = createRequestor({
extensions: [
retry({
retries: 5,
// 指数退避:第 n 次重试延迟 2^n * 100ms
delay: ({ attempt }) => Math.pow(2, attempt) * 100
})
]
})
`
条件重试(仅 5xx 错误)
`typescript
const requestor = createRequestor({
extensions: [
retry({
retries: 3,
delay: 1000,
// 只在服务器错误时重试
retryCondition: ({ lastResponse }) => {
const status = lastResponse?.response?.status
return status >= 500 && status < 600
}
})
]
})
`
---
$3
限制同时发起的请求数量,适用于批量请求场景。
#### 基础用法
`typescript
import { createRequestor, concurrent } from '@net-vert/core'
const requestor = createRequestor({
extensions: [
concurrent({
parallelCount: 3 // 最多同时 3 个请求
})
]
})
// 发起 10 个请求,但同时只会执行 3 个
const promises = []
for (let i = 0; i < 10; i++) {
promises.push(requestor.get(/api/data/${i}))
}
await Promise.all(promises)
`
#### 配置选项
`typescript
interface ConcurrentOptions {
/**
* 最大并行请求数
* 默认:4
*/
parallelCount?: number
/**
* 任务唯一标识生成函数
* 默认:基于时间戳 + 随机数
*/
createId?: (config: RequestConfig) => string | number
}
`
#### 高级示例
`typescript
const requestor = createRequestor({
extensions: [
concurrent({
parallelCount: 5,
// 使用请求 URL 作为任务 ID
createId: ({ config }) => config.url
})
]
})
`
#### 与重试组合使用
`typescript
const requestor = createRequestor({
extensions: [
concurrent({ parallelCount: 3 }), // 限制并发数
retry({ retries: 2, delay: 500 }) // 失败重试
]
})
// 批量请求,每个请求都有重试保护
const results = await Promise.all(
urls.map(url => requestor.get(url))
)
`
---
🔗 中间件组合
多个中间件可以自由组合,执行顺序遵循数组顺序:
`typescript
const requestor = createRequestor({
extensions: [
idempotent(), // 1. 防止并发重复
cache({ duration: 5000 }), // 2. 缓存结果
retry({ retries: 3, delay: 1000 }), // 3. 失败重试
concurrent({ parallelCount: 3 }) // 4. 限制并发
]
})
`
$3
在组合中间件时,建议遵循以下顺序(从前到后):
1. 自定义拦截中间件(日志、鉴权等)
2. idempotent() - 幂等处理
3. cache() - 缓存
4. retry() - 重试
5. concurrent() - 并发控制
$3
#### 1. 数据查询场景(幂等 + 缓存)
`typescript
const requestor = createRequestor({
extensions: [
idempotent(), // 防止并发重复请求
cache({ duration: 5000 }) // 缓存 5 秒
]
})
`
#### 2. 批量请求场景(并发控制 + 重试)
`typescript
const requestor = createRequestor({
extensions: [
concurrent({ parallelCount: 5 }), // 最多同时 5 个请求
retry({ retries: 3, delay: 500 }) // 失败重试 3 次
]
})
`
---
🎯 便捷组合方法
为常见场景提供了预设组合:
$3
创建带缓存和幂等的请求器,适用于数据查询接口。
`typescript
import { createCachedIdempotentRequestor } from '@net-vert/core'
const requestor = createCachedIdempotentRequestor({
duration: 5000, // 缓存 5 秒
persist: false, // 不持久化
// 支持所有 cache 和 idempotent 的配置项
})
// 等价于:
// createRequestor({
// extensions: [
// idempotent(),
// cache({ duration: 5000, persist: false })
// ]
// })
`
$3
创建带并发控制和重试的请求器,适用于批量请求场景。
`typescript
import { createConcurrentRetryRequestor } from '@net-vert/core'
const requestor = createConcurrentRetryRequestor({
parallelCount: 5, // 最多 5 个并发
retries: 3, // 重试 3 次
delay: 1000 // 每次延迟 1 秒
})
// 等价于:
// createRequestor({
// extensions: [
// concurrent({ parallelCount: 5 }),
// retry({ retries: 3, delay: 1000 })
// ]
// })
`
---
🔑 多实例管理
支持注入和管理多个请求器实例:
`typescript
import { inject, createRequestor } from '@net-vert/core'
// 注入主实例(默认)
inject(axiosAdapter)
// 注入备用实例
inject(fetchAdapter, 'backup')
// 使用默认实例
const requestor1 = createRequestor()
// 使用备用实例
const requestor2 = createRequestor({ instanceKey: 'backup' })
`
---
📘 API 参考
$3
#### inject(requestor, instanceKey?)
注入请求器到全局容器。
- requestor: (config: RequestConfig) => Promise - 请求器函数
- instanceKey: string | symbol - 实例标识(可选,默认为 'default')
#### useRequestor(instanceKey?)
获取已注入的请求器。
- instanceKey: string | symbol - 实例标识(可选)
- 返回: Requestor - 请求器对象
#### createRequestor(config?)
创建带中间件的请求器。
- config.extensions: Middleware[] - 中间件数组
- config.instanceKey: string | symbol - 使用的请求器实例标识
- 返回: Requestor - 增强后的请求器
$3
`typescript
interface Requestor {
request(config: RequestConfig): Promise
get(url: string, config?: Omit, 'method' | 'url'>): Promise
post(url: string, data?: D, config?: Omit, 'method' | 'url'>): Promise
put(url: string, data?: D, config?: Omit, 'method' | 'url'>): Promise
delete(url: string, config?: Omit, 'method' | 'url'>): Promise
}
`
---
🎨 完整示例
$3
`typescript
import axios from 'axios'
import { inject, createRequestor, idempotent, cache, retry } from '@net-vert/core'
// 1. 创建并注入 axios 实例
const instance = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000
})
inject(config => instance.request(config))
// 2. 创建数据查询请求器(带缓存和幂等)
export const queryRequestor = createRequestor({
extensions: [
idempotent(),
cache({
duration: 30000, // 缓存 30 秒
persist: true // 持久化
})
]
})
// 3. 创建数据变更请求器(带重试)
export const mutationRequestor = createRequestor({
extensions: [
retry({
retries: 3,
delay: ({ attempt }) => Math.pow(2, attempt) * 200,
retryCondition: ({ lastResponse }) => {
// 只在网络错误或 5xx 时重试
const status = lastResponse?.response?.status
return !status || (status >= 500 && status < 600)
}
})
]
})
// 4. 使用
async function fetchUserProfile(userId: number) {
return queryRequestor.get(/users/${userId})
}
async function updateUserProfile(userId: number, data: any) {
return mutationRequestor.put(/users/${userId}, data)
}
`
$3
`typescript
import { createRequestor, concurrent, retry } from '@net-vert/core'
const uploadRequestor = createRequestor({
extensions: [
concurrent({ parallelCount: 3 }), // 同时最多 3 个上传
retry({ retries: 2, delay: 1000 }) // 失败重试 2 次
]
})
async function uploadFiles(files: File[]) {
const tasks = files.map(file => {
const formData = new FormData()
formData.append('file', file)
return uploadRequestor.post('/upload', formData)
})
return Promise.all(tasks)
}
`
$3
`typescript
import { createRequestor, type Middleware } from '@net-vert/core'
// 自定义日志中间件
const loggerMiddleware: Middleware = async ({ config, next }) => {
console.log('Request:', config.method, config.url)
const startTime = Date.now()
try {
const result = await next()
console.log('Success:', Date.now() - startTime, 'ms')
return result
} catch (error) {
console.error('Error:', error)
throw error
}
}
// 使用自定义中间件
const requestor = createRequestor({
extensions: [
loggerMiddleware,
cache({ duration: 5000 })
]
})
`
---
🔧 高级用法
$3
你可以编写自己的中间件来扩展功能:
`typescript
import { createRequestor, type Middleware } from '@net-vert/core'
// 自定义日志中间件
const loggerMiddleware: Middleware = async ({ config, next }) => {
console.log('Request:', config.method, config.url)
const startTime = Date.now()
try {
const result = await next()
console.log('Success:', Date.now() - startTime, 'ms')
return result
} catch (error) {
console.error('Error:', error)
throw error
}
}
// 使用自定义中间件
const requestor = createRequestor({
extensions: [loggerMiddleware]
})
`
> 💡 提示:如果你的自定义中间件需要拦截所有请求(如日志记录、鉴权检查等),建议将其放在中间件数组的最前面。否则,被前置中间件(如 cache)拦截的请求不会经过你的自定义中间件。
$3
`typescript
import { inject, useRequestor } from '@net-vert/core'
// 注入多个请求器
inject(axiosAdapter, 'axios')
inject(fetchAdapter, 'fetch')
// 动态选择
function getRequestor(type: 'axios' | 'fetch') {
return useRequestor(type)
}
// 使用
const requestor = getRequestor('axios')
requestor.get('/api/data')
`
---
🧪 测试支持
轻松进行单元测试:
`typescript
import { inject, createRequestor, cache } from '@net-vert/core'
import { vi } from 'vitest'
describe('API Tests', () => {
it('should cache requests', async () => {
// 创建 mock 请求器
const mockRequestor = vi.fn(async (config) => ({
code: 200,
data: { url: config.url }
}))
inject(mockRequestor)
const requestor = createRequestor({
extensions: [cache({ duration: 5000 })]
})
// 发起两次相同请求
await requestor.get('/api/test')
await requestor.get('/api/test')
// 验证只调用了一次
expect(mockRequestor).toHaveBeenCalledTimes(1)
})
})
``