---
alwaysApply: true
---
## UseRequestCache 使用规则

### 🎯 核心原则
- **类型安全**：确保 `requestFn`、`params`、`transformResult` 的类型定义完整准确
- **缓存策略明确**：根据数据特性选择合适的存储策略（memory/localStorage/sessionStorage/indexedDB）
- **Key 唯一性**：`cacheKey` 必须能唯一标识请求，避免缓存冲突
- **去重友好**：利用内置的请求去重机制，避免并发重复请求

### 📝 使用规范

#### 1. 基础用法
```typescript
import { useRequestCache } from 'xn-fe-tools'
import { computed, ref } from 'vue'

const userId = ref(1)

const { data, loading, run, refresh, clearCache } = useRequestCache({
  // 必填：请求函数
  requestFn: (params) => fetchUserInfo(params),
  // 必填：缓存 Key（支持字符串或函数）
  cacheKey: params => `user-info-${params.id}`,
  // 可选：响应式参数
  params: computed(() => ({ id: userId.value })),
  // 可选：缓存时间（默认 5 分钟）
  cacheTime: 5 * 60 * 1000,
  // 可选：存储策略
  storage: 'memory',
  // 可选：是否立即执行（默认 true）
  immediate: true,
})
```

#### 2. 参数说明
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|-------|------|------|--------|------|
| `requestFn` | `(params: P) => Promise<R>` | ✅ | - | 实际发起请求的异步函数 |
| `cacheKey` | `string \| (params: P) => string` | ✅ | - | 缓存唯一标识，函数形式可根据参数动态生成 |
| `params` | `MaybeRefOrGetter<P>` | ❌ | - | 请求参数，响应式时自动触发请求 |
| `cacheTime` | `CacheTimeValue` | ❌ | `300000` | 缓存时间。`true` = 永久，`false`/`0` = 禁用，数字 = 毫秒数，默认 5 分钟 |
| `storage` | `MaybeRefOrGetter<CacheStrategy>` | ❌ | `'memory'` | 存储策略：memory/localStorage/sessionStorage/indexedDB |
| `storageKeyPrefix` | `string` | ❌ | `'req_cache_'` | 持久化存储的 Key 前缀 |
| `immediate` | `boolean` | ❌ | `true` | 是否初始化时立即执行 |
| `transformResult` | `(data: R) => T` | ❌ | - | 数据转换函数，转换后的结果会被缓存 |
| `forceRefreshOnFirst` | `MaybeRefOrGetter<boolean>` | ❌ | `false` | 首次请求是否强制跳过缓存 |
| `dbName` | `string` | ❌ | `'RequestCacheDB'` | IndexedDB 数据库名称 |
| `storeName` | `string` | ❌ | `'request_cache'` | IndexedDB 存储对象名称 |
| `onSuccess` | `(data: T) => void` | ❌ | - | 请求成功回调（去重时每个调用处都会触发） |
| `onError` | `(error: any) => void` | ❌ | - | 请求失败回调（去重时每个调用处都会触发） |

#### 3. 返回值说明
| 属性名 | 类型 | 说明 |
|-------|------|------|
| `data` | `Ref<T \| undefined>` | 请求返回的数据（已转换） |
| `loading` | `Ref<boolean>` | 是否正在请求中 |
| `error` | `Ref<any>` | 请求错误信息 |
| `run` | `() => Promise<T>` | 手动触发请求（优先读取缓存） |
| `refresh` | `() => Promise<T>` | 强制刷新（忽略缓存） |
| `clearCache` | `(key?: string) => Promise<void>` | 清除缓存，不传参则清除当前 Key |

#### 4. 全局函数（模块级，不依赖组件实例）

| 函数 | 说明 |
|------|------|
| `setRequestCache(entries)` | 初始化阶段预注入静态缓存数据，注入后对应 `cacheKey` 的 `run()` 直接命中缓存不发请求 |
| `clearRequestCache(filter?)` | 清除注册表中匹配的缓存，不传参清除全部 |
| `getRequestCache(filter?)` | 获取注册表中匹配缓存的快照列表（`CacheSnapshot[]`） |

**`setRequestCache` 参数（`SetRequestCacheEntry`）：**
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `key` | `string` | - | **必填**。与对应实例的 `cacheKey` 保持一致 |
| `data` | `unknown` | - | **必填**。要注入的原始数据 |
| `cacheTime` | `CacheTimeValue` | `true` | 缓存时间。`true`/不传 = 永久，`false`/`0` = 禁用，数字 = 毫秒数 |
| `storage` | `CacheStrategy` | `'memory'` | 存储策略 |
| `storageKeyPrefix` | `string` | `'req_cache_'` | storage key 前缀 |

```typescript
// main.ts — 项目初始化阶段预注入
import { setRequestCache } from 'xn-fe-tools'

await setRequestCache([
  { key: 'user-profile', data: window.__APP_CONFIG__.user },
  { key: 'permissions',  data: window.__APP_CONFIG__.permissions },
])
app.mount('#app')

// 组件内正常使用，run() 直接命中预注入的缓存
const { data } = useRequestCache({
  requestFn: fetchUserProfile,
  cacheKey: 'user-profile',
})
```

#### 6. 存储策略选择
```typescript
// 内存缓存（默认）- 页面刷新后失效，适合临时数据
storage: 'memory'

// localStorage - 永久存储，适合不常变化的配置数据
storage: 'localStorage'

// sessionStorage - 标签页关闭后失效，适合会话级数据（如枚举字典）
storage: 'sessionStorage'

// indexedDB - 异步存储，适合大数据量缓存
storage: 'indexedDB'
```

#### 7. 动态 cacheKey 示例
```typescript
// ✅ 正确：根据参数动态生成 Key
cacheKey: params => `user-${params.id}-${params.type}`

// ✅ 正确：静态 Key（参数不影响缓存标识时使用）
cacheKey: 'global-config'

// ❌ 错误：Key 不唯一，可能导致缓存冲突
cacheKey: 'user' // 不同用户会返回相同缓存
```

### 🚨 注意事项
- **cacheKey 必须唯一**：不同请求使用相同 Key 会导致缓存数据错乱
- **params 响应式**：如需参数变化自动触发请求，请使用 `computed` 或 getter 函数包装
- **transformResult 时机**：转换后的数据会被缓存，下次命中缓存直接返回转换后结果
- **去重机制**：并发相同 Key 的请求只会发送一次网络请求，但每个调用的回调都会触发
- **IndexedDB 限制**：indexedDB 模式下数据不同步到内存，每次都从 DB 读取
- **SSR 兼容**：服务端渲染时自动降级为 memory 模式
- **setRequestCache key 对齐**：预注入的 `key` 必须与对应 `useRequestCache` 实例的 `cacheKey` 完全一致，否则不会命中缓存
- **setRequestCache 默认永久**：`cacheTime` 默认 `true`（永久），适合静态配置；需要过期的数据请显式传入毫秒数
- **cacheTime 速记值**：`true` = 永久，`false`/`0` = 禁用缓存（每次发请求），数字 = 毫秒数

### 🧪 测试建议
- 测试缓存命中与过期逻辑
- 测试请求去重（并发多次相同请求）
- 测试各种存储策略的读写
- 测试 `transformResult` 数据转换
- 测试 `forceRefreshOnFirst` 首次强制刷新
- 测试回调队列（onSuccess/onError）在去重场景下的执行

### 💡 最佳实践

#### 应用初始化阶段预注入静态数据
```typescript
// main.ts
await setRequestCache([
  { key: 'user-profile', data: window.__APP_CONFIG__.user },
  { key: 'permissions',  data: window.__APP_CONFIG__.permissions },
])
app.mount('#app')
```

#### 封装枚举/字典选择器
```typescript
// 使用 sessionStorage 缓存枚举，页面刷新后仍可复用
const { data } = useRequestCache({
  cacheKey: `enum-${enumKey}`,
  requestFn: () => fetchEnum(enumKey),
  storage: 'sessionStorage',
  cacheTime: 30 * 60 * 1000,
  transformResult: list => list.map(item => ({ label: item.name, value: item.id })),
})
```

#### 大数据缓存
```typescript
// 使用 indexedDB 存储大量数据，避免内存占用
const { data } = useRequestCache({
  cacheKey: 'heavy-list',
  requestFn: fetchLargeList,
  storage: 'indexedDB',
  cacheTime: 12 * 60 * 60 * 1000,
})
```

### 🔗 相关链接
- [使用文档](../hooks/useRequestCache.md)
