@qiaopeng/tanstack-query-plus 0.5.0 → 0.5.2
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 +443 -68
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
10. [第八步:智能预取](#10-第八步智能预取)
|
|
17
17
|
11. [第九步:Suspense 模式](#11-第九步suspense-模式)
|
|
18
18
|
12. [第十步:离线支持与持久化](#12-第十步离线支持与持久化)
|
|
19
|
-
13. [
|
|
20
|
-
14. [
|
|
21
|
-
15. [
|
|
19
|
+
13. [第十一步:数据防护与安全](#13-第十一步数据防护与安全)
|
|
20
|
+
14. [第十二步:焦点管理](#14-第十二步焦点管理)
|
|
21
|
+
15. [第十三步:工具函数与选择器](#15-第十三步工具函数与选择器)
|
|
22
|
+
16. [最佳实践与常见问题](#16-最佳实践与常见问题)
|
|
23
|
+
17. [API 索引](#17-api-索引)
|
|
22
24
|
|
|
23
25
|
---
|
|
24
26
|
|
|
@@ -42,6 +44,46 @@
|
|
|
42
44
|
|
|
43
45
|
接下来,让我们一步步学习如何使用这些功能。
|
|
44
46
|
|
|
47
|
+
### 1.1 设计初衷与原则
|
|
48
|
+
|
|
49
|
+
- 保持与 TanStack Query v5 完全兼容,不改变其核心行为,只做“安全增强”。
|
|
50
|
+
- 提供开箱即用的最佳实践配置,减少重复劳动与认知负担。
|
|
51
|
+
- 以“安全”为首要前提:数据防护、持久化安全、离线队列的稳健性、错误处理的可控性。
|
|
52
|
+
- API 设计坚持渐进增强:原生用法不变,增强能力按需启用,便于迁移和学习。
|
|
53
|
+
- TypeScript 友好:导出类型与范型参数与 TanStack 保持一致,避免类型陷阱。
|
|
54
|
+
|
|
55
|
+
### 1.2 适用场景
|
|
56
|
+
|
|
57
|
+
- 中大型前端应用,需要统一的查询管理与最佳实践配置。
|
|
58
|
+
- 有离线需求(电商、文档编辑、移动端 Web)或需要缓存持久化与恢复的场景。
|
|
59
|
+
- 需要更强的乐观更新、并发冲突处理、数据一致性保障。
|
|
60
|
+
- 希望最小化自定义基础设施代码,将精力聚焦在业务逻辑。
|
|
61
|
+
|
|
62
|
+
### 1.3 非目标与边界
|
|
63
|
+
|
|
64
|
+
- 不替代后端的并发控制与数据一致性保障;前端 Data Guard 仅作为“最后防线”。
|
|
65
|
+
- 不内置与具体后端协议的强绑定(如 GraphQL/REST 的特定实现);保持通用。
|
|
66
|
+
- 不存储任何敏感凭据;持久化仅针对查询缓存,且可配置与可关闭。
|
|
67
|
+
|
|
68
|
+
### 1.4 安全与合规
|
|
69
|
+
|
|
70
|
+
- 持久化默认仅保存可序列化且成功的查询数据,避免异常对象导致恢复失败(参见 `createPersistOptions`)。
|
|
71
|
+
- 建议不要在 `queryKey` 中包含敏感信息(如 token、身份证号、手机号原文)。
|
|
72
|
+
- DevTools 仅在开发环境启用,避免在生产泄露内部状态(参见 `isDevToolsEnabled`)。
|
|
73
|
+
- 离线队列持久化时去除了函数体,仅存操作元数据;实际执行函数需通过注册表安全绑定。
|
|
74
|
+
|
|
75
|
+
### 1.5 术语速览
|
|
76
|
+
|
|
77
|
+
- `queryKey`:查询的唯一标识;必须是稳定、可序列化的值(通常为数组)
|
|
78
|
+
- `queryFn`:实际获取数据的异步函数;返回 Promise
|
|
79
|
+
- `staleTime`:数据保持“新鲜”的时间窗口;新鲜期内不会重复请求
|
|
80
|
+
- `gcTime`:缓存保留时间;超过后缓存会被清理
|
|
81
|
+
- `invalidate`:标记查询为过期;下一次渲染或焦点恢复时会重新请求
|
|
82
|
+
- `refetch`:主动重新请求数据
|
|
83
|
+
- `persist`:将查询缓存持久化到存储(localStorage/IndexedDB)并在刷新后恢复
|
|
84
|
+
- `offline`:网络不可用时的状态;本库提供队列与自动恢复机制
|
|
85
|
+
- `optimistic update`:先更新 UI,再与服务端同步;失败时需回滚
|
|
86
|
+
- `Data Guard`:防止旧数据覆盖新数据的前端机制(版本/时间戳/哈希比对)
|
|
45
87
|
---
|
|
46
88
|
|
|
47
89
|
## 2. 安装与环境准备
|
|
@@ -69,6 +111,9 @@ npm install @tanstack/react-query-devtools
|
|
|
69
111
|
|
|
70
112
|
# 视口预取功能(如果需要 useInViewPrefetch)
|
|
71
113
|
npm install react-intersection-observer
|
|
114
|
+
|
|
115
|
+
# 路由预取示例所需(如果使用 useRoutePrefetch 示例中的 Link/useNavigate)
|
|
116
|
+
npm install react-router-dom
|
|
72
117
|
```
|
|
73
118
|
|
|
74
119
|
### 2.3 环境要求
|
|
@@ -80,11 +125,104 @@ npm install react-intersection-observer
|
|
|
80
125
|
|
|
81
126
|
现在环境准备好了,让我们开始配置应用。
|
|
82
127
|
|
|
128
|
+
### 2.4 学习路径与检查清单
|
|
129
|
+
|
|
130
|
+
严格建议按照以下顺序学习与落地,并在每一步完成后进行自检:
|
|
131
|
+
|
|
132
|
+
1. 安装依赖:确保安装本库及 peer 依赖(`@tanstack/react-query`、`react`、`react-dom`),按需安装 `devtools`、`react-intersection-observer`、`react-router-dom`
|
|
133
|
+
2. 创建 `QueryClient`:使用 `GLOBAL_QUERY_CONFIG`,避免随意调整 `retry`、`staleTime` 造成请求风暴
|
|
134
|
+
3. 包裹应用:使用 `PersistQueryClientProvider` 开启持久化与离线支持(生产环境建议保留持久化)
|
|
135
|
+
4. 添加 DevTools(开发环境):`isDevToolsEnabled()` 控制显示,严禁在生产强制开启
|
|
136
|
+
5. 发起首个查询:优先使用 `useEnhancedQuery`,在慢查询或错误场景验证日志输出
|
|
137
|
+
6. 增强 Mutation:在列表 CRUD 场景启用乐观更新,并验证回滚路径与错误处理
|
|
138
|
+
7. 离线与持久化:断网测试页面行为;验证缓存恢复与离线队列的稳健性
|
|
139
|
+
8. 数据防护(可选但推荐):开启 Data Guard 的版本/时间戳/哈希策略,防止旧数据覆盖
|
|
140
|
+
9. 焦点管理:按照业务需要控制窗口聚焦时的刷新频率,避免抖动
|
|
141
|
+
10. 预取:根据网络情况与页面流量,按需启用悬停/视口/路由/空闲预取,避免过度预取
|
|
142
|
+
|
|
143
|
+
完成以上 10 点后,再进入“最佳实践与常见问题”章节进行整体检查与性能、安全优化。
|
|
144
|
+
|
|
145
|
+
### 2.5 五分钟上手示例
|
|
146
|
+
|
|
147
|
+
目标:用 5 分钟完成“配置 Provider + 首个查询 + DevTools 调试”。
|
|
148
|
+
|
|
149
|
+
1. 安装依赖:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm install @qiaopeng/tanstack-query-plus @tanstack/react-query @tanstack/react-query-persist-client
|
|
153
|
+
npm install @tanstack/react-query-devtools --save-dev
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
2. 创建 Provider:
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
// main.tsx
|
|
160
|
+
import { QueryClient, PersistQueryClientProvider } from '@qiaopeng/tanstack-query-plus'
|
|
161
|
+
import { GLOBAL_QUERY_CONFIG } from '@qiaopeng/tanstack-query-plus/core'
|
|
162
|
+
import { ReactQueryDevtools, isDevToolsEnabled } from '@qiaopeng/tanstack-query-plus/core/devtools'
|
|
163
|
+
|
|
164
|
+
const queryClient = new QueryClient({ defaultOptions: GLOBAL_QUERY_CONFIG })
|
|
165
|
+
|
|
166
|
+
function Providers({ children }) {
|
|
167
|
+
return (
|
|
168
|
+
<PersistQueryClientProvider client={queryClient}>
|
|
169
|
+
{children}
|
|
170
|
+
{isDevToolsEnabled() && <ReactQueryDevtools initialIsOpen={false} />}
|
|
171
|
+
</PersistQueryClientProvider>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
3. 发起首个查询:
|
|
177
|
+
|
|
178
|
+
```tsx
|
|
179
|
+
// App.tsx
|
|
180
|
+
import { useEnhancedQuery } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
181
|
+
|
|
182
|
+
export default function App() {
|
|
183
|
+
const { data, isLoading, isError } = useEnhancedQuery({
|
|
184
|
+
queryKey: ['hello'],
|
|
185
|
+
queryFn: async () => ({ message: 'Hello Query Plus' }),
|
|
186
|
+
})
|
|
187
|
+
if (isLoading) return <div>加载中...</div>
|
|
188
|
+
if (isError) return <div>加载失败</div>
|
|
189
|
+
return <div>{data.message}</div>
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
4. 跑起来:在浏览器中打开 DevTools 面板,查看 `['hello']` 查询状态。
|
|
194
|
+
|
|
195
|
+
### 2.6 TypeScript 配置建议
|
|
196
|
+
|
|
197
|
+
以下 `tsconfig.json` 选项可以帮助初学者避免常见类型问题:
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"compilerOptions": {
|
|
202
|
+
"target": "ES2020",
|
|
203
|
+
"module": "ESNext",
|
|
204
|
+
"moduleResolution": "Node",
|
|
205
|
+
"jsx": "react-jsx",
|
|
206
|
+
"strict": true,
|
|
207
|
+
"skipLibCheck": true,
|
|
208
|
+
"esModuleInterop": true,
|
|
209
|
+
"forceConsistentCasingInFileNames": true,
|
|
210
|
+
"resolveJsonModule": true,
|
|
211
|
+
"isolatedModules": true,
|
|
212
|
+
"useUnknownInCatchVariables": true
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
说明:
|
|
218
|
+
- `strict: true` 有助于暴露隐含的 `any` 与未处理的 `undefined`
|
|
219
|
+
- `skipLibCheck: true` 可避免第三方库类型检查的噪音(对本库安全)
|
|
220
|
+
- `useUnknownInCatchVariables` 提醒你显式处理错误类型
|
|
221
|
+
|
|
83
222
|
---
|
|
84
223
|
|
|
85
224
|
## 3. 第一步:配置 Provider
|
|
86
225
|
|
|
87
|
-
|
|
88
226
|
任何使用 TanStack Query 的应用都需要一个 Provider 来提供 QueryClient 实例。本库提供了一个增强版的 Provider,让配置变得更简单。
|
|
89
227
|
|
|
90
228
|
### 3.1 最简配置
|
|
@@ -819,33 +957,43 @@ mutation.mutate({ newTitle: '新标题' })
|
|
|
819
957
|
|
|
820
958
|
### 7.5 条件性乐观更新
|
|
821
959
|
|
|
822
|
-
|
|
960
|
+
本库未提供单独的 `useConditionalOptimisticMutation`。如需按条件启用乐观更新,使用以下两种安全模式:
|
|
961
|
+
|
|
962
|
+
1. 使用两个 mutation,根据条件选择调用哪个(最清晰、类型安全):
|
|
823
963
|
|
|
824
964
|
```tsx
|
|
825
|
-
import {
|
|
965
|
+
import { useMutation } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
826
966
|
|
|
827
|
-
const
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
// 第三个参数:配置选项
|
|
833
|
-
{
|
|
834
|
-
mutationKey: ['updateTodo'], // 可选的 mutation key
|
|
835
|
-
optimistic: {
|
|
836
|
-
queryKey: ['todos'],
|
|
837
|
-
updater: (oldTodos, updatedTodo) =>
|
|
838
|
-
oldTodos?.map(t => t.id === updatedTodo.id ? { ...t, ...updatedTodo } : t)
|
|
839
|
-
},
|
|
840
|
-
onSuccess: () => {
|
|
841
|
-
console.log('更新成功')
|
|
842
|
-
}
|
|
967
|
+
const optimisticUpdate = useMutation({
|
|
968
|
+
mutationFn: updateTodo,
|
|
969
|
+
optimistic: {
|
|
970
|
+
queryKey: ['todos'],
|
|
971
|
+
updater: (oldTodos, updatedTodo) => oldTodos?.map(t => t.id === updatedTodo.id ? { ...t, ...updatedTodo } : t)
|
|
843
972
|
}
|
|
844
|
-
)
|
|
973
|
+
})
|
|
845
974
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
975
|
+
const plainUpdate = useMutation({ mutationFn: updateTodo })
|
|
976
|
+
|
|
977
|
+
function save(todo) {
|
|
978
|
+
const shouldOptimistic = todo.priority === 'high'
|
|
979
|
+
const runner = shouldOptimistic ? optimisticUpdate : plainUpdate
|
|
980
|
+
runner.mutate(todo)
|
|
981
|
+
}
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
2. 基于状态切换 `optimistic.enabled`(适合全局开关):
|
|
985
|
+
|
|
986
|
+
```tsx
|
|
987
|
+
// 以应用自身配置或组件状态为准(此处仅示例)
|
|
988
|
+
const enableOptimistic = true
|
|
989
|
+
const mutation = useMutation({
|
|
990
|
+
mutationFn: updateTodo,
|
|
991
|
+
optimistic: {
|
|
992
|
+
queryKey: ['todos'],
|
|
993
|
+
enabled: enableOptimistic,
|
|
994
|
+
updater: (oldTodos, updatedTodo) => oldTodos?.map(t => t.id === updatedTodo.id ? { ...t, ...updatedTodo } : t)
|
|
995
|
+
}
|
|
996
|
+
})
|
|
849
997
|
```
|
|
850
998
|
|
|
851
999
|
### 7.6 列表操作的简化 Mutation
|
|
@@ -897,20 +1045,38 @@ function TodoList() {
|
|
|
897
1045
|
|
|
898
1046
|
### 7.7 批量 Mutation
|
|
899
1047
|
|
|
900
|
-
|
|
1048
|
+
本库未提供 `useBatchMutation`。进行批量操作时,推荐两种模式:
|
|
1049
|
+
|
|
1050
|
+
1. 在一个 mutation 中封装批量逻辑(一次请求或并发 Promise):
|
|
901
1051
|
|
|
902
1052
|
```tsx
|
|
903
|
-
import {
|
|
1053
|
+
import { useMutation } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
904
1054
|
|
|
905
|
-
const
|
|
906
|
-
async (
|
|
907
|
-
|
|
908
|
-
|
|
1055
|
+
const batchDelete = useMutation({
|
|
1056
|
+
mutationFn: async (ids: string[]) => {
|
|
1057
|
+
return Promise.all(ids.map(id => api.deleteTodo(id)))
|
|
1058
|
+
},
|
|
1059
|
+
optimistic: {
|
|
1060
|
+
queryKey: ['todos'],
|
|
1061
|
+
updater: (old, ids: string[]) => old?.filter(t => !ids.includes(String(t.id)))
|
|
909
1062
|
}
|
|
910
|
-
)
|
|
1063
|
+
})
|
|
911
1064
|
|
|
912
1065
|
// 使用
|
|
913
|
-
|
|
1066
|
+
batchDelete.mutate(['id1', 'id2', 'id3'])
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
2. 使用离线队列在恢复网络后批量执行(稳健且可持久化):
|
|
1070
|
+
|
|
1071
|
+
```tsx
|
|
1072
|
+
import { createOfflineQueueManager, mutationRegistry } from '@qiaopeng/tanstack-query-plus/features'
|
|
1073
|
+
|
|
1074
|
+
const queue = createOfflineQueueManager({ storageKey: 'todo-ops', concurrency: 3 })
|
|
1075
|
+
|
|
1076
|
+
function registerDelete(id: string) {
|
|
1077
|
+
mutationRegistry.register(['todos','delete',id].join('-'), () => api.deleteTodo(id))
|
|
1078
|
+
queue.add({ mutationKey: ['todos','delete',id], mutationFn: () => api.deleteTodo(id), priority: 1 })
|
|
1079
|
+
}
|
|
914
1080
|
```
|
|
915
1081
|
|
|
916
1082
|
### 7.8 乐观更新工具函数
|
|
@@ -968,7 +1134,7 @@ const list7 = conditionalUpdateItems(
|
|
|
968
1134
|
)
|
|
969
1135
|
```
|
|
970
1136
|
|
|
971
|
-
|
|
1137
|
+
### 7.9 完整示例:Todo 应用
|
|
972
1138
|
|
|
973
1139
|
```tsx
|
|
974
1140
|
import { useEnhancedQuery, useMutation } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
@@ -1042,6 +1208,13 @@ function TodoApp() {
|
|
|
1042
1208
|
}
|
|
1043
1209
|
```
|
|
1044
1210
|
|
|
1211
|
+
### 7.10 安全提示
|
|
1212
|
+
|
|
1213
|
+
- 明确回滚路径:在 `onError` 或 `rollback` 中恢复缓存或触发重新拉取
|
|
1214
|
+
- 稳定的 `queryKey`:使用 Key 工厂,避免结构漂移导致更新不到位
|
|
1215
|
+
- 变量安全:Mutation 变量不包含敏感信息(如 token),错误上报需脱敏
|
|
1216
|
+
- 冲突处理:对 409 触发家族失效与 UI 提示;对 500 展示兜底提示并记录错误
|
|
1217
|
+
|
|
1045
1218
|
现在你已经掌握了数据变更和乐观更新。接下来,让我们学习如何处理无限滚动和分页场景。
|
|
1046
1219
|
|
|
1047
1220
|
---
|
|
@@ -2023,6 +2196,14 @@ function ProductBrowser() {
|
|
|
2023
2196
|
|
|
2024
2197
|
现在你已经掌握了预取策略。接下来,让我们学习 Suspense 模式,它可以让你的代码更简洁。
|
|
2025
2198
|
|
|
2199
|
+
### 10.12 安全提示
|
|
2200
|
+
|
|
2201
|
+
- 结合网络状况:使用 `useSmartPrefetch` 在慢网络禁用预取,避免拥塞
|
|
2202
|
+
- 控制频率与间隔:为悬停/路由预取设置 `minInterval`,避免重复请求
|
|
2203
|
+
- 严格限定 Key:预取目标必须是稳定且可序列化的 `queryKey`
|
|
2204
|
+
- 避免敏感信息:不要将敏感数据拼入 `queryKey`
|
|
2205
|
+
- 可回收:在复杂页面中适时清理预取历史(`clearPrefetchHistory`)以避免状态膨胀
|
|
2206
|
+
|
|
2026
2207
|
---
|
|
2027
2208
|
|
|
2028
2209
|
## 11. 第九步:Suspense 模式
|
|
@@ -2498,9 +2679,11 @@ async function handleUpdateUser(userData) {
|
|
|
2498
2679
|
} else {
|
|
2499
2680
|
// 在线时直接执行
|
|
2500
2681
|
await updateUserAPI(userData)
|
|
2501
|
-
|
|
2682
|
+
}
|
|
2502
2683
|
}
|
|
2503
2684
|
|
|
2685
|
+
### 队列管理器操作参考
|
|
2686
|
+
|
|
2504
2687
|
// 获取队列状态
|
|
2505
2688
|
const state = queueManager.getState()
|
|
2506
2689
|
console.log({
|
|
@@ -2644,14 +2827,118 @@ async function checkAndMigrate() {
|
|
|
2644
2827
|
|
|
2645
2828
|
现在你已经掌握了离线支持。接下来,让我们学习焦点管理,它可以优化用户切换标签页时的体验。
|
|
2646
2829
|
|
|
2830
|
+
### 12.9 安全提示与 SSR 注意
|
|
2831
|
+
|
|
2832
|
+
- 持久化范围:仅持久化成功且可序列化的查询数据(默认行为),避免异常对象恢复失败
|
|
2833
|
+
- 敏感信息:严禁将敏感凭据放入缓存数据或 `queryKey`
|
|
2834
|
+
- 离线队列:仅持久化操作元信息,不持久化函数体;真实执行需通过 `mutationRegistry` 注册,避免函数闭包泄露
|
|
2835
|
+
- 存储容量:`checkStorageSize` 超过 5MB 建议迁移到 IndexedDB;定期 `clearExpiredCache`
|
|
2836
|
+
- SSR 注意:服务端渲染环境下 `PersistQueryClientProvider` 会自动降级为普通 Provider,浏览器 API 均有守卫,不会在 Node 环境访问 `window`
|
|
2837
|
+
|
|
2838
|
+
---
|
|
2839
|
+
|
|
2840
|
+
## 13. 第十一步:数据防护与安全
|
|
2841
|
+
|
|
2842
|
+
在多人协作或高并发的应用中,数据一致性是一个常见挑战。例如:
|
|
2843
|
+
- **旧数据覆盖新数据**:用户 A 打开页面,用户 B 更新了数据,用户 A 保存时可能会覆盖 B 的更改。
|
|
2844
|
+
- **乐观更新冲突**:前端乐观更新了数据,但后端因为版本冲突拒绝了请求。
|
|
2845
|
+
- **竞态条件**:网络请求乱序返回,导致旧数据覆盖了新数据。
|
|
2846
|
+
|
|
2847
|
+
本库提供了 **Data Guard** 机制来解决这些问题。
|
|
2848
|
+
|
|
2849
|
+
### 13.1 查询数据防护
|
|
2850
|
+
|
|
2851
|
+
使用 `useDataGuardQueryConfig` 可以自动检测并拒绝过期的服务端数据:
|
|
2852
|
+
|
|
2853
|
+
```tsx
|
|
2854
|
+
import { useEnhancedQuery, useDataGuardQueryConfig } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
2855
|
+
|
|
2856
|
+
function ProductList() {
|
|
2857
|
+
// 自动包装查询配置
|
|
2858
|
+
const config = useDataGuardQueryConfig(
|
|
2859
|
+
['products'],
|
|
2860
|
+
fetchProducts,
|
|
2861
|
+
{
|
|
2862
|
+
// 策略配置
|
|
2863
|
+
maxDataAge: 5000, // 允许的最大数据时差
|
|
2864
|
+
onStaleDataDetected: ({ reason, cached, rejected }) => {
|
|
2865
|
+
console.warn(`拒绝了旧数据: ${reason}`)
|
|
2866
|
+
// cached: 当前缓存的较新数据
|
|
2867
|
+
// rejected: 被拒绝的服务端旧数据
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
)
|
|
2871
|
+
|
|
2872
|
+
const { data } = useEnhancedQuery(config)
|
|
2873
|
+
|
|
2874
|
+
return <div>...</div>
|
|
2875
|
+
}
|
|
2876
|
+
```
|
|
2877
|
+
|
|
2878
|
+
**防护策略优先级**:
|
|
2879
|
+
1. **版本号 (Version)**: 如果数据包含 `version` 字段,严格比较版本号。
|
|
2880
|
+
2. **时间戳 (Timestamp)**: 如果数据包含 `updatedAt` 字段,比较更新时间。
|
|
2881
|
+
3. **哈希 (Hash)**: 如果都没有,计算内容哈希,拒绝相同内容的重复更新。
|
|
2882
|
+
|
|
2883
|
+
### 13.2 变更数据防护
|
|
2884
|
+
|
|
2885
|
+
使用 `useDataGuardMutation` 处理并发更新冲突:
|
|
2886
|
+
|
|
2887
|
+
```tsx
|
|
2888
|
+
import { useDataGuardMutation } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
2889
|
+
|
|
2890
|
+
function EditProduct({ product }) {
|
|
2891
|
+
const mutation = useDataGuardMutation(
|
|
2892
|
+
(updates) => api.updateProduct(updates),
|
|
2893
|
+
['products'], // 相关的查询 key
|
|
2894
|
+
{
|
|
2895
|
+
// 发生冲突时的回调 (HTTP 409)
|
|
2896
|
+
onConflict: (error) => {
|
|
2897
|
+
toast.error('检测到数据冲突,页面将自动刷新')
|
|
2898
|
+
},
|
|
2899
|
+
// 内置乐观更新支持
|
|
2900
|
+
optimistic: {
|
|
2901
|
+
queryKey: ['products'],
|
|
2902
|
+
updater: (old, newProduct) => {
|
|
2903
|
+
// Data Guard 会自动处理版本号递增和时间戳更新
|
|
2904
|
+
return { ...old, ...newProduct }
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
)
|
|
2909
|
+
|
|
2910
|
+
const handleSave = (updates) => {
|
|
2911
|
+
// 提交时带上当前版本号
|
|
2912
|
+
mutation.mutate({
|
|
2913
|
+
...updates,
|
|
2914
|
+
version: product.version
|
|
2915
|
+
})
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
```
|
|
2919
|
+
|
|
2920
|
+
**`useDataGuardMutation` 的特性**:
|
|
2921
|
+
- **自动冲突检测**:捕获 409 Conflict 错误并触发回调。
|
|
2922
|
+
- **智能乐观更新**:自动递增本地数据的版本号,防止 UI 闪烁。
|
|
2923
|
+
- **家族元数据同步**:更新成功后,自动同步相关查询元数据。
|
|
2924
|
+
- **自动失效**:发生冲突时,自动失效相关缓存以获取最新数据。
|
|
2925
|
+
|
|
2926
|
+
### 13.3 安全提示
|
|
2927
|
+
|
|
2928
|
+
- 统一数据版本:后端需提供 `version` 或 `updatedAt` 字段,前端 Data Guard 才能更有效地比对
|
|
2929
|
+
- 明确冲突策略:将 409 视为冲突并提示用户刷新,避免静默覆盖
|
|
2930
|
+
- 审计与日志:为冲突与拒绝旧数据的情况打点,便于后续排查
|
|
2931
|
+
- 仅在必要处开启 Data Guard:对只读数据可关闭防护以减少开销
|
|
2932
|
+
- 家族同步慎用:确保变体的 `queryKey` 有共同前缀,避免误伤无关查询
|
|
2933
|
+
|
|
2647
2934
|
---
|
|
2648
2935
|
|
|
2649
|
-
##
|
|
2936
|
+
## 14. 第十二步:焦点管理
|
|
2650
2937
|
|
|
2651
2938
|
|
|
2652
2939
|
当用户切换浏览器标签页或窗口时,TanStack Query 默认会在窗口重新获得焦点时刷新数据。本库提供了更精细的焦点管理功能。
|
|
2653
2940
|
|
|
2654
|
-
###
|
|
2941
|
+
### 14.1 获取焦点状态
|
|
2655
2942
|
|
|
2656
2943
|
```tsx
|
|
2657
2944
|
import { useFocusState, usePageVisibility } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
@@ -2669,7 +2956,7 @@ function FocusIndicator() {
|
|
|
2669
2956
|
}
|
|
2670
2957
|
```
|
|
2671
2958
|
|
|
2672
|
-
###
|
|
2959
|
+
### 14.2 焦点恢复时刷新指定查询
|
|
2673
2960
|
|
|
2674
2961
|
默认情况下,所有查询都会在窗口聚焦时刷新。但有时你只想刷新特定的查询:
|
|
2675
2962
|
|
|
@@ -2691,7 +2978,7 @@ function Dashboard() {
|
|
|
2691
2978
|
}
|
|
2692
2979
|
```
|
|
2693
2980
|
|
|
2694
|
-
###
|
|
2981
|
+
### 14.3 焦点恢复时执行回调
|
|
2695
2982
|
|
|
2696
2983
|
```tsx
|
|
2697
2984
|
import { useFocusCallback } from '@qiaopeng/tanstack-query-plus/hooks'
|
|
@@ -2712,7 +2999,7 @@ function AnalyticsTracker() {
|
|
|
2712
2999
|
}
|
|
2713
3000
|
```
|
|
2714
3001
|
|
|
2715
|
-
###
|
|
3002
|
+
### 14.4 条件性焦点刷新
|
|
2716
3003
|
|
|
2717
3004
|
只在满足条件时刷新:
|
|
2718
3005
|
|
|
@@ -2731,7 +3018,7 @@ function ChatRoom({ roomId, isActive }) {
|
|
|
2731
3018
|
}
|
|
2732
3019
|
```
|
|
2733
3020
|
|
|
2734
|
-
###
|
|
3021
|
+
### 14.5 暂停焦点管理
|
|
2735
3022
|
|
|
2736
3023
|
在某些场景下(如模态框打开时),你可能想暂停焦点刷新:
|
|
2737
3024
|
|
|
@@ -2769,7 +3056,7 @@ function VideoPlayer() {
|
|
|
2769
3056
|
}
|
|
2770
3057
|
```
|
|
2771
3058
|
|
|
2772
|
-
###
|
|
3059
|
+
### 14.6 智能焦点管理器
|
|
2773
3060
|
|
|
2774
3061
|
获取焦点管理的统计信息:
|
|
2775
3062
|
|
|
@@ -2796,7 +3083,7 @@ function FocusDebugPanel() {
|
|
|
2796
3083
|
}
|
|
2797
3084
|
```
|
|
2798
3085
|
|
|
2799
|
-
###
|
|
3086
|
+
### 14.7 焦点管理最佳实践
|
|
2800
3087
|
|
|
2801
3088
|
1. **设置 minInterval**:避免用户频繁切换标签页时过度刷新
|
|
2802
3089
|
2. **选择性刷新**:不是所有数据都需要在焦点恢复时刷新
|
|
@@ -2807,12 +3094,12 @@ function FocusDebugPanel() {
|
|
|
2807
3094
|
|
|
2808
3095
|
---
|
|
2809
3096
|
|
|
2810
|
-
##
|
|
3097
|
+
## 15. 第十三步:工具函数与选择器
|
|
2811
3098
|
|
|
2812
3099
|
|
|
2813
3100
|
本库提供了丰富的工具函数,帮助你更高效地处理数据。
|
|
2814
3101
|
|
|
2815
|
-
###
|
|
3102
|
+
### 15.1 选择器(Selectors)
|
|
2816
3103
|
|
|
2817
3104
|
选择器用于 `select` 选项,可以在数据返回后进行转换。注意:大部分选择器是高阶函数,需要先调用生成实际的选择器函数。
|
|
2818
3105
|
|
|
@@ -2875,7 +3162,7 @@ const { data: userBasicInfo } = useQuery({
|
|
|
2875
3162
|
})
|
|
2876
3163
|
```
|
|
2877
3164
|
|
|
2878
|
-
###
|
|
3165
|
+
### 15.2 组合选择器
|
|
2879
3166
|
|
|
2880
3167
|
选择器可以组合使用:
|
|
2881
3168
|
|
|
@@ -2903,7 +3190,7 @@ const { data: adminEmails } = useQuery({
|
|
|
2903
3190
|
})
|
|
2904
3191
|
```
|
|
2905
3192
|
|
|
2906
|
-
###
|
|
3193
|
+
### 15.3 独立使用选择器函数
|
|
2907
3194
|
|
|
2908
3195
|
选择器也可以独立使用。注意:这些函数大多是高阶函数,需要先传入参数生成选择器,再传入数据:
|
|
2909
3196
|
|
|
@@ -2949,7 +3236,7 @@ const activeNamesSelector = compose(
|
|
|
2949
3236
|
const activeNames = activeNamesSelector(users) // ['Alice', 'Charlie']
|
|
2950
3237
|
```
|
|
2951
3238
|
|
|
2952
|
-
###
|
|
3239
|
+
### 15.4 列表更新工具
|
|
2953
3240
|
|
|
2954
3241
|
用于乐观更新的列表操作:
|
|
2955
3242
|
|
|
@@ -2989,7 +3276,7 @@ const batchRemoved = batchRemoveItems(todos, ['1', '3'])
|
|
|
2989
3276
|
const reordered = reorderItems(todos, 0, 2)
|
|
2990
3277
|
```
|
|
2991
3278
|
|
|
2992
|
-
###
|
|
3279
|
+
### 15.5 创建乐观更新配置
|
|
2993
3280
|
|
|
2994
3281
|
快速创建常用的乐观更新配置:
|
|
2995
3282
|
|
|
@@ -3019,7 +3306,7 @@ const addMutation = useMutation({
|
|
|
3019
3306
|
})
|
|
3020
3307
|
```
|
|
3021
3308
|
|
|
3022
|
-
###
|
|
3309
|
+
### 15.6 Query Key 工具
|
|
3023
3310
|
|
|
3024
3311
|
```tsx
|
|
3025
3312
|
import {
|
|
@@ -3071,7 +3358,7 @@ const normalized = normalizeQueryParams(
|
|
|
3071
3358
|
) // { page: 1, sort: 'name' }
|
|
3072
3359
|
```
|
|
3073
3360
|
|
|
3074
|
-
###
|
|
3361
|
+
### 15.7 网络工具
|
|
3075
3362
|
|
|
3076
3363
|
```tsx
|
|
3077
3364
|
import {
|
|
@@ -3105,7 +3392,7 @@ const info = getNetworkInfo()
|
|
|
3105
3392
|
// }
|
|
3106
3393
|
```
|
|
3107
3394
|
|
|
3108
|
-
###
|
|
3395
|
+
### 15.8 存储工具
|
|
3109
3396
|
|
|
3110
3397
|
```tsx
|
|
3111
3398
|
import {
|
|
@@ -3139,7 +3426,7 @@ cloned.nested.value = 2
|
|
|
3139
3426
|
console.log(original.nested.value) // 1(原始数据不变)
|
|
3140
3427
|
```
|
|
3141
3428
|
|
|
3142
|
-
###
|
|
3429
|
+
### 15.9 字段映射工具
|
|
3143
3430
|
|
|
3144
3431
|
```tsx
|
|
3145
3432
|
import {
|
|
@@ -3176,7 +3463,7 @@ const newTodo = {
|
|
|
3176
3463
|
|
|
3177
3464
|
**注意**:`createFieldEnricher` 是一个高级函数,用于根据配置数据丰富查询结果中的字段(如将 ID 映射为名称),需要配合 QueryClient 使用,适用于特定的业务场景。
|
|
3178
3465
|
|
|
3179
|
-
###
|
|
3466
|
+
### 15.10 保持上一次数据
|
|
3180
3467
|
|
|
3181
3468
|
在数据刷新时保持显示上一次的数据:
|
|
3182
3469
|
|
|
@@ -3200,7 +3487,7 @@ function SearchResults({ query }) {
|
|
|
3200
3487
|
}
|
|
3201
3488
|
```
|
|
3202
3489
|
|
|
3203
|
-
###
|
|
3490
|
+
### 15.11 家族一致性工具
|
|
3204
3491
|
|
|
3205
3492
|
在某些高级场景下,你可能需要自行枚举并同步同一资源的家族查询变体(分页/筛选/排序等)。本库提供了工具函数用于匹配与安全同步:
|
|
3206
3493
|
|
|
@@ -3234,7 +3521,7 @@ function useManualFamilySync() {
|
|
|
3234
3521
|
|
|
3235
3522
|
---
|
|
3236
3523
|
|
|
3237
|
-
##
|
|
3524
|
+
## 16. 最佳实践与常见问题
|
|
3238
3525
|
|
|
3239
3526
|
### 导入路径速查表
|
|
3240
3527
|
|
|
@@ -3264,7 +3551,7 @@ function useManualFamilySync() {
|
|
|
3264
3551
|
- 如果需要 TanStack Query 的原生 `useQuery`(而非增强版),从 `@tanstack/react-query` 导入
|
|
3265
3552
|
- 子路径导入可以实现更好的 tree-shaking
|
|
3266
3553
|
|
|
3267
|
-
###
|
|
3554
|
+
### 16.1 项目结构建议
|
|
3268
3555
|
|
|
3269
3556
|
```
|
|
3270
3557
|
src/
|
|
@@ -3286,7 +3573,7 @@ src/
|
|
|
3286
3573
|
└── App.tsx
|
|
3287
3574
|
```
|
|
3288
3575
|
|
|
3289
|
-
###
|
|
3576
|
+
### 16.2 封装自定义 Hooks
|
|
3290
3577
|
|
|
3291
3578
|
将查询逻辑封装成自定义 hooks:
|
|
3292
3579
|
|
|
@@ -3321,7 +3608,7 @@ function UserProfile({ userId }) {
|
|
|
3321
3608
|
}
|
|
3322
3609
|
```
|
|
3323
3610
|
|
|
3324
|
-
###
|
|
3611
|
+
### 16.3 配置最佳实践
|
|
3325
3612
|
|
|
3326
3613
|
```tsx
|
|
3327
3614
|
// config/queryClient.ts
|
|
@@ -3345,7 +3632,7 @@ export const queryClient = new QueryClient({
|
|
|
3345
3632
|
})
|
|
3346
3633
|
```
|
|
3347
3634
|
|
|
3348
|
-
###
|
|
3635
|
+
### 16.4 错误处理最佳实践
|
|
3349
3636
|
|
|
3350
3637
|
```tsx
|
|
3351
3638
|
// 全局错误处理
|
|
@@ -3374,7 +3661,7 @@ const queryClient = new QueryClient({
|
|
|
3374
3661
|
})
|
|
3375
3662
|
```
|
|
3376
3663
|
|
|
3377
|
-
###
|
|
3664
|
+
### 16.5 TypeScript 类型最佳实践
|
|
3378
3665
|
|
|
3379
3666
|
```tsx
|
|
3380
3667
|
import type {
|
|
@@ -3415,7 +3702,7 @@ function useUpdateUser() {
|
|
|
3415
3702
|
}
|
|
3416
3703
|
```
|
|
3417
3704
|
|
|
3418
|
-
###
|
|
3705
|
+
### 16.6 常见问题解答
|
|
3419
3706
|
|
|
3420
3707
|
#### Q: DevTools 报错 "Module not found"
|
|
3421
3708
|
|
|
@@ -3523,7 +3810,7 @@ useEnhancedQuery({
|
|
|
3523
3810
|
```
|
|
3524
3811
|
3. 检查 queryKey 是否正确(使用 key 工厂避免拼写错误)
|
|
3525
3812
|
|
|
3526
|
-
###
|
|
3813
|
+
### 16.7 性能优化建议
|
|
3527
3814
|
|
|
3528
3815
|
1. **合理设置 staleTime**:避免不必要的重复请求
|
|
3529
3816
|
2. **使用 select**:只选择需要的数据,减少重渲染
|
|
@@ -3532,9 +3819,9 @@ useEnhancedQuery({
|
|
|
3532
3819
|
5. **懒加载**:结合 Suspense 和代码分割
|
|
3533
3820
|
6. **避免过度乐观更新**:只在必要时使用
|
|
3534
3821
|
|
|
3535
|
-
###
|
|
3822
|
+
### 16.8 安全建议
|
|
3536
3823
|
|
|
3537
|
-
1. **不要在 queryKey 中包含敏感信息**:queryKey 可能被记录或暴露
|
|
3824
|
+
1. **不要在 queryKey 中包含敏感信息**:queryKey 可能被记录或暴露
|
|
3538
3825
|
2. **验证服务端响应**:不要盲目信任 API 返回的数据
|
|
3539
3826
|
3. **处理认证过期**:在全局错误处理中处理 401 错误
|
|
3540
3827
|
4. **清理敏感缓存**:用户登出时清除缓存
|
|
@@ -3554,8 +3841,9 @@ useEnhancedQuery({
|
|
|
3554
3841
|
7. ✅ 智能预取策略
|
|
3555
3842
|
8. ✅ Suspense 模式
|
|
3556
3843
|
9. ✅ 离线支持和持久化
|
|
3557
|
-
10. ✅
|
|
3558
|
-
11. ✅
|
|
3844
|
+
10. ✅ 数据防护与安全
|
|
3845
|
+
11. ✅ 焦点管理
|
|
3846
|
+
12. ✅ 工具函数和选择器
|
|
3559
3847
|
|
|
3560
3848
|
### 下一步
|
|
3561
3849
|
|
|
@@ -3564,3 +3852,90 @@ useEnhancedQuery({
|
|
|
3564
3852
|
- 在 [Issues](https://github.com/qiaopengg/qiaopeng-tanstack-query-plus/issues) 中提问或反馈
|
|
3565
3853
|
|
|
3566
3854
|
祝你编码愉快!🚀
|
|
3855
|
+
### 16.9 类型与错误处理规范
|
|
3856
|
+
|
|
3857
|
+
- 明确类型参数:在增强 hooks 中显式标注 `TData` 与 `TError`,避免 `any` 漏出
|
|
3858
|
+
- 统一错误模型:为后端错误定义统一类型(如 `ApiError`),在 UI 层集中处理
|
|
3859
|
+
- 不吞错误:日志与监控采集应在开发开启,生产使用采样与脱敏
|
|
3860
|
+
- 优先 `isError` 分支渲染兜底组件,避免在 `data` 为 `undefined` 时解构引发异常
|
|
3861
|
+
- 乐观更新的回滚必须可重入:错误重试不应导致状态错乱
|
|
3862
|
+
|
|
3863
|
+
### 16.10 SSR 注意事项
|
|
3864
|
+
|
|
3865
|
+
- Provider 降级:SSR 环境自动使用普通 `QueryClientProvider`,浏览器 API 有环境守卫
|
|
3866
|
+
- 数据注入:如需 SSR 注水,建议结合 TanStack 原生脱水/注水(本库不强绑)
|
|
3867
|
+
- 路由预取:服务端不执行预取相关浏览器 API,需在客户端挂载后进行
|
|
3868
|
+
|
|
3869
|
+
### 16.11 Tree-shaking 与导入路径
|
|
3870
|
+
|
|
3871
|
+
- 使用子路径导入(如 `@qiaopeng/tanstack-query-plus/hooks`、`/core`、`/utils`),提升摇树效果
|
|
3872
|
+
- 保持副作用为零:本包 `sideEffects: false`,按需导入可以减少体积
|
|
3873
|
+
- 避免从根入口导入全部模块:仅在需要时按子路径引入具体能力
|
|
3874
|
+
|
|
3875
|
+
### 16.12 起步排障清单
|
|
3876
|
+
|
|
3877
|
+
- DevTools 未显示:安装并从 `@qiaopeng/tanstack-query-plus/core/devtools` 导入,仅在开发环境显示(`core/devtools.ts:28`)
|
|
3878
|
+
- 视口预取报错:安装 `react-intersection-observer` 并从 `hooks/inview` 子路径导入(`src/hooks/useInViewPrefetch.ts`)
|
|
3879
|
+
- 路由预取报错:安装 `react-router-dom`,示例仅依赖其 Link/useNavigate
|
|
3880
|
+
- 缓存未恢复:检查 `enablePersistence` 与 `cacheKey`;确认浏览器支持 localStorage(`features/persistence.ts:43`)
|
|
3881
|
+
- 离线队列不执行:确保在恢复网络后调用 `createOfflineQueueManager`,并通过 `mutationRegistry` 注册函数(`features/offline.ts:30`)
|
|
3882
|
+
- 乐观更新错乱:确保 `queryKey` 稳定、列表更新器使用 `id` 对齐(`utils/optimisticUtils.ts:14`)
|
|
3883
|
+
- 冲突未处理:确认后端返回 409 或约定错误码,前端使用 `useDataGuardMutation` 捕获(`hooks/useDataGuardMutation.ts:80`)
|
|
3884
|
+
- SSR 环境错误:确保 Provider 自动降级,不在服务端访问 `window`(`PersistQueryClientProvider.tsx:34`)
|
|
3885
|
+
|
|
3886
|
+
### 16.13 生产前检查清单
|
|
3887
|
+
|
|
3888
|
+
- DevTools:生产环境关闭(`isDevToolsEnabled()` 为 false)
|
|
3889
|
+
- 错误处理:所有查询与变更都有兜底 UI 与日志记录
|
|
3890
|
+
- 缓存策略:为高频接口设置合理 `staleTime`,避免重复请求
|
|
3891
|
+
- 乐观更新:具备回滚与重试策略;冲突触发家族失效
|
|
3892
|
+
- 持久化:确认缓存大小与迁移策略(localStorage → IndexedDB)
|
|
3893
|
+
- 安全审查:queryKey 与缓存不包含敏感信息;队列不持久化函数体
|
|
3894
|
+
- 监控:慢查询上报、错误采样与脱敏处理
|
|
3895
|
+
|
|
3896
|
+
## 17. API 索引
|
|
3897
|
+
|
|
3898
|
+
为方便查找,这里列出各子路径的主要导出与用途:
|
|
3899
|
+
|
|
3900
|
+
- `@qiaopeng/tanstack-query-plus`(顶层)
|
|
3901
|
+
- `PersistQueryClientProvider`、`usePersistenceStatus`、`usePersistenceManager`
|
|
3902
|
+
- `QueryClient`、`QueryClientProvider`、`useQueryClient`、`skipToken`、`useIsMutating`(直接再导出原生 API)
|
|
3903
|
+
|
|
3904
|
+
- `@qiaopeng/tanstack-query-plus/core`
|
|
3905
|
+
- 配置:`GLOBAL_QUERY_CONFIG`、`createCustomConfig`、`DEFAULT_STALE_TIME`、`DEFAULT_GC_TIME`
|
|
3906
|
+
- 重试:`defaultQueryRetryStrategy`、`defaultMutationRetryStrategy`、`exponentialBackoff`
|
|
3907
|
+
- 环境:`isDev`、`isProd`、`isTest`
|
|
3908
|
+
- DevTools:`ReactQueryDevtools`、`isDevToolsEnabled`、`createDevToolsConfig`(src/core/devtools.ts:28)
|
|
3909
|
+
- 焦点管理:`focusManager`、`getSmartFocusManager`、`pauseFocusManager`、`resumeFocusManager`
|
|
3910
|
+
- Key 工具:`queryKeys`、`normalizeQueryKey`、`createDomainKeyFactory`、`createMutationKeyFactory`
|
|
3911
|
+
- Query 配置:`createAppQueryOptions`、`createListQueryOptions`
|
|
3912
|
+
|
|
3913
|
+
- `@qiaopeng/tanstack-query-plus/hooks`
|
|
3914
|
+
- 查询:`useEnhancedQuery`、`useEnhancedSuspenseQuery`、`useEnhancedInfiniteQuery`
|
|
3915
|
+
- 批量查询:`useEnhancedQueries`、`useAutoRefreshBatchQueries`、`useDashboardQueries`
|
|
3916
|
+
- Mutation:`useMutation`、`useListMutation`、`setupMutationDefaults`
|
|
3917
|
+
- 预取:`useHoverPrefetch`、`useInViewPrefetch`(子路径 `hooks/inview`)、`useRoutePrefetch`、`useSmartPrefetch`、`useConditionalPrefetch`、`useIdlePrefetch`、`usePeriodicPrefetch`、`usePredictivePrefetch`、`usePriorityPrefetch`
|
|
3918
|
+
- 焦点:`useFocusState`、`useFocusRefetch`、`useConditionalFocusRefetch`、`usePauseFocus`、`useSmartFocusManager`
|
|
3919
|
+
- 数据防护:`useDataGuardQueryConfig`、`useDataGuardMutation`
|
|
3920
|
+
|
|
3921
|
+
- `@qiaopeng/tanstack-query-plus/features`
|
|
3922
|
+
- 离线:`setupOnlineManager`、`isOnline`、`createOfflineQueueManager`、`OfflineQueueManager`、`mutationRegistry`、`subscribeToOnlineStatus`
|
|
3923
|
+
- 持久化:`createPersistOptions`、`createPersister`、`clearCache`、`clearExpiredCache`、`checkStorageSize`、`getStorageStats`、`migrateToIndexedDB`
|
|
3924
|
+
|
|
3925
|
+
- `@qiaopeng/tanstack-query-plus/components`
|
|
3926
|
+
- Loading:`DefaultLoadingFallback`、`FullScreenLoading`、`ListSkeletonFallback`、`PageSkeletonFallback`、`SmallLoadingIndicator`、`TextSkeletonFallback`
|
|
3927
|
+
- 错误边界:`QueryErrorBoundary`
|
|
3928
|
+
- Suspense:`SuspenseWrapper`、`QuerySuspenseWrapper`
|
|
3929
|
+
|
|
3930
|
+
- `@qiaopeng/tanstack-query-plus/utils`
|
|
3931
|
+
- 选择器:`selectById`、`selectFields`、`compose` 等
|
|
3932
|
+
- 列表/乐观工具:`listUpdater`、`createAddItemConfig`、`createUpdateItemConfig`、`reorderItems`
|
|
3933
|
+
- 预取管理器:`getPrefetchManager`、`SmartPrefetchManager`、`resetPrefetchManager`
|
|
3934
|
+
- Query Key 工厂:`createQueryKeyFactory`、`normalizeQueryParams`
|
|
3935
|
+
- 存储与网络:`isStorageAvailable`、`getNetworkSpeed`、`isSlowNetwork`
|
|
3936
|
+
- 数据防护:`applyDataGuard`、`updateFamilyMetadata`
|
|
3937
|
+
|
|
3938
|
+
- `@qiaopeng/tanstack-query-plus/react-query`
|
|
3939
|
+
- 原生 API 再导出:`useQuery`、`useMutation`、`useInfiniteQuery`、`useSuspenseQuery` 等(src/react-query/index.ts:1)
|
|
3940
|
+
|
|
3941
|
+
提示:完整导出列表可在 `package.json:33` 的 `exports` 字段中查看;顶层入口再导出常用原生 API,子路径按模块分层导出,便于 tree-shaking。
|
package/dist/index.d.ts
CHANGED
|
@@ -4,5 +4,5 @@ export * from "./hooks/index.js";
|
|
|
4
4
|
export { PersistQueryClientProvider, usePersistenceStatus, usePersistenceManager, type PersistQueryClientProviderProps } from "./PersistQueryClientProvider.js";
|
|
5
5
|
export * from "./types/index.js";
|
|
6
6
|
export * from "./utils/index.js";
|
|
7
|
-
export { QueryClient, QueryClientProvider, skipToken, useQueryClient, useIsMutating } from "@tanstack/react-query";
|
|
7
|
+
export { QueryClient, QueryClientProvider, skipToken, useQueryClient, useIsMutating, type UseMutationOptions, type UseQueryOptions } from "@tanstack/react-query";
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,KAAK,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAChK,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,KAAK,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAChK,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
package/package.json
CHANGED