ccbot-cli 2.0.1 → 2.1.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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/bin/adapters/claude.js +150 -0
  3. package/bin/adapters/codex.js +439 -0
  4. package/bin/install.js +509 -349
  5. package/bin/lib/ccline.js +82 -0
  6. package/bin/lib/utils.js +87 -34
  7. package/bin/uninstall.js +48 -0
  8. package/config/AGENTS.md +630 -0
  9. package/config/CLAUDE.md +229 -20
  10. package/config/ccline/config.toml +161 -0
  11. package/config/codex-config.example.toml +22 -0
  12. package/config/settings.example.json +32 -0
  13. package/output-styles/abyss-cultivator.md +399 -0
  14. package/package.json +14 -5
  15. package/skills/SKILL.md +159 -0
  16. package/skills/domains/ai/SKILL.md +34 -0
  17. package/skills/domains/ai/agent-dev.md +242 -0
  18. package/skills/domains/ai/llm-security.md +288 -0
  19. package/skills/domains/ai/prompt-and-eval.md +279 -0
  20. package/skills/domains/ai/rag-system.md +542 -0
  21. package/skills/domains/architecture/SKILL.md +42 -0
  22. package/skills/domains/architecture/api-design.md +225 -0
  23. package/skills/domains/architecture/caching.md +299 -0
  24. package/skills/domains/architecture/cloud-native.md +285 -0
  25. package/skills/domains/architecture/message-queue.md +329 -0
  26. package/skills/domains/architecture/security-arch.md +297 -0
  27. package/skills/domains/data-engineering/SKILL.md +207 -0
  28. package/skills/domains/development/SKILL.md +46 -0
  29. package/skills/domains/development/cpp.md +246 -0
  30. package/skills/domains/development/go.md +323 -0
  31. package/skills/domains/development/java.md +277 -0
  32. package/skills/domains/development/python.md +288 -0
  33. package/skills/domains/development/rust.md +313 -0
  34. package/skills/domains/development/shell.md +313 -0
  35. package/skills/domains/development/typescript.md +277 -0
  36. package/skills/domains/devops/SKILL.md +39 -0
  37. package/skills/domains/devops/cost-optimization.md +272 -0
  38. package/skills/domains/devops/database.md +217 -0
  39. package/skills/domains/devops/devsecops.md +198 -0
  40. package/skills/domains/devops/git-workflow.md +181 -0
  41. package/skills/domains/devops/observability.md +280 -0
  42. package/skills/domains/devops/performance.md +336 -0
  43. package/skills/domains/devops/testing.md +283 -0
  44. package/skills/domains/frontend-design/SKILL.md +38 -0
  45. package/skills/domains/frontend-design/claymorphism/SKILL.md +119 -0
  46. package/skills/domains/frontend-design/claymorphism/references/tokens.css +52 -0
  47. package/skills/domains/frontend-design/component-patterns.md +202 -0
  48. package/skills/domains/frontend-design/engineering.md +287 -0
  49. package/skills/domains/frontend-design/glassmorphism/SKILL.md +140 -0
  50. package/skills/domains/frontend-design/glassmorphism/references/tokens.css +32 -0
  51. package/skills/domains/frontend-design/liquid-glass/SKILL.md +137 -0
  52. package/skills/domains/frontend-design/liquid-glass/references/tokens.css +81 -0
  53. package/skills/domains/frontend-design/neubrutalism/SKILL.md +143 -0
  54. package/skills/domains/frontend-design/neubrutalism/references/tokens.css +44 -0
  55. package/skills/domains/frontend-design/state-management.md +680 -0
  56. package/skills/domains/frontend-design/ui-aesthetics.md +110 -0
  57. package/skills/domains/frontend-design/ux-principles.md +156 -0
  58. package/skills/domains/infrastructure/SKILL.md +200 -0
  59. package/skills/domains/mobile/SKILL.md +224 -0
  60. package/skills/domains/orchestration/SKILL.md +29 -0
  61. package/skills/domains/orchestration/multi-agent.md +263 -0
  62. package/skills/domains/security/SKILL.md +54 -0
  63. package/skills/domains/security/blue-team.md +436 -0
  64. package/skills/domains/security/code-audit.md +265 -0
  65. package/skills/domains/security/pentest.md +226 -0
  66. package/skills/domains/security/red-team.md +375 -0
  67. package/skills/domains/security/threat-intel.md +372 -0
  68. package/skills/domains/security/vuln-research.md +369 -0
  69. package/skills/orchestration/multi-agent/SKILL.md +493 -0
  70. package/skills/run_skill.js +129 -0
  71. package/skills/tools/gen-docs/SKILL.md +116 -0
  72. package/skills/tools/gen-docs/scripts/doc_generator.js +435 -0
  73. package/skills/tools/lib/shared.js +98 -0
  74. package/skills/tools/verify-change/SKILL.md +140 -0
  75. package/skills/tools/verify-change/scripts/change_analyzer.js +289 -0
  76. package/skills/tools/verify-module/SKILL.md +127 -0
  77. package/skills/tools/verify-module/scripts/module_scanner.js +171 -0
  78. package/skills/tools/verify-quality/SKILL.md +160 -0
  79. package/skills/tools/verify-quality/scripts/quality_checker.js +337 -0
  80. package/skills/tools/verify-security/SKILL.md +143 -0
  81. package/skills/tools/verify-security/scripts/security_scanner.js +283 -0
  82. package/bin/lib/registry.js +0 -61
  83. package/config/.claudeignore +0 -11
@@ -0,0 +1,680 @@
1
+ ---
2
+ name: state-management
3
+ description: 前端状态管理技术。Redux、Zustand、Jotai、Recoil、Context API、状态选择决策。当用户提到状态管理、Redux、Zustand、Jotai、Recoil、全局状态、状态同步时使用。
4
+ ---
5
+
6
+ # 🎨 🗂️ 状态管理 · State Management
7
+
8
+ ## 状态管理对比
9
+
10
+ | 框架 | 模式 | 学习曲线 | 性能 | 适用场景 |
11
+ |------|------|----------|------|----------|
12
+ | Redux | Flux | 陡峭 | 中 | 大型应用、复杂状态 |
13
+ | Zustand | Flux-like | 平缓 | 高 | 中小型应用、快速开发 |
14
+ | Jotai | Atomic | 平缓 | 高 | 细粒度更新、原子化状态 |
15
+ | Recoil | Atomic | 中等 | 高 | React生态、派生状态 |
16
+ | Context | Provider | 简单 | 低 | 简单共享、主题配置 |
17
+ | MobX | Reactive | 中等 | 高 | OOP风格、自动追踪 |
18
+
19
+ ## 选择决策树
20
+
21
+ ```
22
+ 需要状态管理?
23
+
24
+ ├─ 简单主题/配置 → Context API
25
+
26
+ ├─ 中小型应用
27
+ │ ├─ 喜欢简洁 → Zustand
28
+ │ └─ 需要原子化 → Jotai
29
+
30
+ └─ 大型应用
31
+ ├─ 团队熟悉Redux → Redux Toolkit
32
+ ├─ 需要时间旅行 → Redux DevTools
33
+ ├─ 复杂派生状态 → Recoil
34
+ └─ OOP风格 → MobX
35
+ ```
36
+
37
+ ## Redux Toolkit (推荐)
38
+
39
+ ### 基础配置
40
+
41
+ ```typescript
42
+ // store.ts
43
+ import { configureStore } from '@reduxjs/toolkit'
44
+ import counterReducer from './features/counter/counterSlice'
45
+ import userReducer from './features/user/userSlice'
46
+
47
+ export const store = configureStore({
48
+ reducer: {
49
+ counter: counterReducer,
50
+ user: userReducer,
51
+ },
52
+ middleware: (getDefaultMiddleware) =>
53
+ getDefaultMiddleware({
54
+ serializableCheck: {
55
+ ignoredActions: ['user/setTimestamp'],
56
+ },
57
+ }),
58
+ })
59
+
60
+ export type RootState = ReturnType<typeof store.getState>
61
+ export type AppDispatch = typeof store.dispatch
62
+ ```
63
+
64
+ ### Slice 定义
65
+
66
+ ```typescript
67
+ // counterSlice.ts
68
+ import { createSlice, PayloadAction } from '@reduxjs/toolkit'
69
+
70
+ interface CounterState {
71
+ value: number
72
+ status: 'idle' | 'loading' | 'failed'
73
+ }
74
+
75
+ const initialState: CounterState = {
76
+ value: 0,
77
+ status: 'idle',
78
+ }
79
+
80
+ export const counterSlice = createSlice({
81
+ name: 'counter',
82
+ initialState,
83
+ reducers: {
84
+ increment: (state) => {
85
+ state.value += 1
86
+ },
87
+ decrement: (state) => {
88
+ state.value -= 1
89
+ },
90
+ incrementByAmount: (state, action: PayloadAction<number>) => {
91
+ state.value += action.payload
92
+ },
93
+ },
94
+ })
95
+
96
+ export const { increment, decrement, incrementByAmount } = counterSlice.actions
97
+ export default counterSlice.reducer
98
+ ```
99
+
100
+ ### 异步 Thunk
101
+
102
+ ```typescript
103
+ // userSlice.ts
104
+ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
105
+
106
+ interface User {
107
+ id: string
108
+ name: string
109
+ email: string
110
+ }
111
+
112
+ export const fetchUser = createAsyncThunk(
113
+ 'user/fetchUser',
114
+ async (userId: string) => {
115
+ const response = await fetch(`/api/users/${userId}`)
116
+ return (await response.json()) as User
117
+ }
118
+ )
119
+
120
+ const userSlice = createSlice({
121
+ name: 'user',
122
+ initialState: {
123
+ data: null as User | null,
124
+ loading: false,
125
+ error: null as string | null,
126
+ },
127
+ reducers: {},
128
+ extraReducers: (builder) => {
129
+ builder
130
+ .addCase(fetchUser.pending, (state) => {
131
+ state.loading = true
132
+ })
133
+ .addCase(fetchUser.fulfilled, (state, action) => {
134
+ state.loading = false
135
+ state.data = action.payload
136
+ })
137
+ .addCase(fetchUser.rejected, (state, action) => {
138
+ state.loading = false
139
+ state.error = action.error.message || 'Failed'
140
+ })
141
+ },
142
+ })
143
+
144
+ export default userSlice.reducer
145
+ ```
146
+
147
+ ### Hooks 使用
148
+
149
+ ```typescript
150
+ // hooks.ts
151
+ import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
152
+ import type { RootState, AppDispatch } from './store'
153
+
154
+ export const useAppDispatch = () => useDispatch<AppDispatch>()
155
+ export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
156
+
157
+ // Component
158
+ import { useAppDispatch, useAppSelector } from './hooks'
159
+ import { increment, fetchUser } from './features/counter/counterSlice'
160
+
161
+ function Counter() {
162
+ const count = useAppSelector((state) => state.counter.value)
163
+ const dispatch = useAppDispatch()
164
+
165
+ return (
166
+ <div>
167
+ <span>{count}</span>
168
+ <button onClick={() => dispatch(increment())}>+</button>
169
+ </div>
170
+ )
171
+ }
172
+ ```
173
+
174
+ ## Zustand (轻量推荐)
175
+
176
+ ### 基础 Store
177
+
178
+ ```typescript
179
+ // store.ts
180
+ import { create } from 'zustand'
181
+
182
+ interface BearState {
183
+ bears: number
184
+ increase: () => void
185
+ decrease: () => void
186
+ reset: () => void
187
+ }
188
+
189
+ export const useBearStore = create<BearState>((set) => ({
190
+ bears: 0,
191
+ increase: () => set((state) => ({ bears: state.bears + 1 })),
192
+ decrease: () => set((state) => ({ bears: state.bears - 1 })),
193
+ reset: () => set({ bears: 0 }),
194
+ }))
195
+
196
+ // Component
197
+ function BearCounter() {
198
+ const bears = useBearStore((state) => state.bears)
199
+ return <h1>{bears} bears</h1>
200
+ }
201
+
202
+ function Controls() {
203
+ const increase = useBearStore((state) => state.increase)
204
+ return <button onClick={increase}>+1</button>
205
+ }
206
+ ```
207
+
208
+ ### 异步 Actions
209
+
210
+ ```typescript
211
+ interface UserStore {
212
+ user: User | null
213
+ loading: boolean
214
+ fetchUser: (id: string) => Promise<void>
215
+ }
216
+
217
+ export const useUserStore = create<UserStore>((set) => ({
218
+ user: null,
219
+ loading: false,
220
+ fetchUser: async (id) => {
221
+ set({ loading: true })
222
+ try {
223
+ const res = await fetch(`/api/users/${id}`)
224
+ const user = await res.json()
225
+ set({ user, loading: false })
226
+ } catch (error) {
227
+ set({ loading: false })
228
+ }
229
+ },
230
+ }))
231
+ ```
232
+
233
+ ### 中间件
234
+
235
+ ```typescript
236
+ import { create } from 'zustand'
237
+ import { persist, devtools } from 'zustand/middleware'
238
+
239
+ interface AuthState {
240
+ token: string | null
241
+ login: (token: string) => void
242
+ logout: () => void
243
+ }
244
+
245
+ export const useAuthStore = create<AuthState>()(
246
+ devtools(
247
+ persist(
248
+ (set) => ({
249
+ token: null,
250
+ login: (token) => set({ token }),
251
+ logout: () => set({ token: null }),
252
+ }),
253
+ {
254
+ name: 'auth-storage',
255
+ }
256
+ )
257
+ )
258
+ )
259
+ ```
260
+
261
+ ### Immer 集成
262
+
263
+ ```typescript
264
+ import { create } from 'zustand'
265
+ import { immer } from 'zustand/middleware/immer'
266
+
267
+ interface TodoState {
268
+ todos: Array<{ id: string; text: string; done: boolean }>
269
+ addTodo: (text: string) => void
270
+ toggleTodo: (id: string) => void
271
+ }
272
+
273
+ export const useTodoStore = create<TodoState>()(
274
+ immer((set) => ({
275
+ todos: [],
276
+ addTodo: (text) =>
277
+ set((state) => {
278
+ state.todos.push({ id: Date.now().toString(), text, done: false })
279
+ }),
280
+ toggleTodo: (id) =>
281
+ set((state) => {
282
+ const todo = state.todos.find((t) => t.id === id)
283
+ if (todo) todo.done = !todo.done
284
+ }),
285
+ }))
286
+ )
287
+ ```
288
+
289
+ ## Jotai (原子化)
290
+
291
+ ### Atom 定义
292
+
293
+ ```typescript
294
+ import { atom } from 'jotai'
295
+
296
+ // 原始 atom
297
+ export const countAtom = atom(0)
298
+
299
+ // 派生 atom (只读)
300
+ export const doubleCountAtom = atom((get) => get(countAtom) * 2)
301
+
302
+ // 派生 atom (读写)
303
+ export const incrementAtom = atom(
304
+ (get) => get(countAtom),
305
+ (get, set) => set(countAtom, get(countAtom) + 1)
306
+ )
307
+
308
+ // 异步 atom
309
+ export const userAtom = atom(async (get) => {
310
+ const userId = get(userIdAtom)
311
+ const response = await fetch(`/api/users/${userId}`)
312
+ return response.json()
313
+ })
314
+ ```
315
+
316
+ ### 使用 Atoms
317
+
318
+ ```typescript
319
+ import { useAtom, useAtomValue, useSetAtom } from 'jotai'
320
+
321
+ function Counter() {
322
+ const [count, setCount] = useAtom(countAtom)
323
+ const doubleCount = useAtomValue(doubleCountAtom)
324
+ const increment = useSetAtom(incrementAtom)
325
+
326
+ return (
327
+ <div>
328
+ <p>Count: {count}</p>
329
+ <p>Double: {doubleCount}</p>
330
+ <button onClick={increment}>+1</button>
331
+ </div>
332
+ )
333
+ }
334
+ ```
335
+
336
+ ### 原子家族
337
+
338
+ ```typescript
339
+ import { atomFamily } from 'jotai/utils'
340
+
341
+ // 为每个 ID 创建独立 atom
342
+ export const todoAtomFamily = atomFamily((id: string) =>
343
+ atom({
344
+ id,
345
+ text: '',
346
+ done: false,
347
+ })
348
+ )
349
+
350
+ function TodoItem({ id }: { id: string }) {
351
+ const [todo, setTodo] = useAtom(todoAtomFamily(id))
352
+
353
+ return (
354
+ <div>
355
+ <input
356
+ value={todo.text}
357
+ onChange={(e) => setTodo({ ...todo, text: e.target.value })}
358
+ />
359
+ <input
360
+ type="checkbox"
361
+ checked={todo.done}
362
+ onChange={(e) => setTodo({ ...todo, done: e.target.checked })}
363
+ />
364
+ </div>
365
+ )
366
+ }
367
+ ```
368
+
369
+ ### 持久化
370
+
371
+ ```typescript
372
+ import { atomWithStorage } from 'jotai/utils'
373
+
374
+ export const themeAtom = atomWithStorage<'light' | 'dark'>('theme', 'light')
375
+
376
+ // 自定义存储
377
+ export const customAtom = atomWithStorage(
378
+ 'custom-key',
379
+ { value: 0 },
380
+ {
381
+ getItem: (key) => {
382
+ const value = localStorage.getItem(key)
383
+ return value ? JSON.parse(value) : { value: 0 }
384
+ },
385
+ setItem: (key, value) => {
386
+ localStorage.setItem(key, JSON.stringify(value))
387
+ },
388
+ removeItem: (key) => {
389
+ localStorage.removeItem(key)
390
+ },
391
+ }
392
+ )
393
+ ```
394
+
395
+ ## Recoil
396
+
397
+ ### Atom 和 Selector
398
+
399
+ ```typescript
400
+ import { atom, selector } from 'recoil'
401
+
402
+ // Atom
403
+ export const textState = atom({
404
+ key: 'textState',
405
+ default: '',
406
+ })
407
+
408
+ // Selector (派生状态)
409
+ export const charCountState = selector({
410
+ key: 'charCountState',
411
+ get: ({ get }) => {
412
+ const text = get(textState)
413
+ return text.length
414
+ },
415
+ })
416
+
417
+ // 异步 Selector
418
+ export const userState = selector({
419
+ key: 'userState',
420
+ get: async ({ get }) => {
421
+ const userId = get(userIdState)
422
+ const response = await fetch(`/api/users/${userId}`)
423
+ return response.json()
424
+ },
425
+ })
426
+ ```
427
+
428
+ ### 使用 Recoil
429
+
430
+ ```typescript
431
+ import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
432
+
433
+ function TextInput() {
434
+ const [text, setText] = useRecoilState(textState)
435
+ const charCount = useRecoilValue(charCountState)
436
+
437
+ return (
438
+ <div>
439
+ <input value={text} onChange={(e) => setText(e.target.value)} />
440
+ <p>Character Count: {charCount}</p>
441
+ </div>
442
+ )
443
+ }
444
+ ```
445
+
446
+ ### Atom Family
447
+
448
+ ```typescript
449
+ import { atomFamily } from 'recoil'
450
+
451
+ export const todoItemState = atomFamily({
452
+ key: 'todoItem',
453
+ default: (id: string) => ({
454
+ id,
455
+ text: '',
456
+ done: false,
457
+ }),
458
+ })
459
+
460
+ function TodoItem({ id }: { id: string }) {
461
+ const [todo, setTodo] = useRecoilState(todoItemState(id))
462
+
463
+ return (
464
+ <input
465
+ value={todo.text}
466
+ onChange={(e) => setTodo({ ...todo, text: e.target.value })}
467
+ />
468
+ )
469
+ }
470
+ ```
471
+
472
+ ## Context API
473
+
474
+ ### 基础 Context
475
+
476
+ ```typescript
477
+ import { createContext, useContext, useState, ReactNode } from 'react'
478
+
479
+ interface ThemeContextType {
480
+ theme: 'light' | 'dark'
481
+ toggleTheme: () => void
482
+ }
483
+
484
+ const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
485
+
486
+ export function ThemeProvider({ children }: { children: ReactNode }) {
487
+ const [theme, setTheme] = useState<'light' | 'dark'>('light')
488
+
489
+ const toggleTheme = () => {
490
+ setTheme((prev) => (prev === 'light' ? 'dark' : 'light'))
491
+ }
492
+
493
+ return (
494
+ <ThemeContext.Provider value={{ theme, toggleTheme }}>
495
+ {children}
496
+ </ThemeContext.Provider>
497
+ )
498
+ }
499
+
500
+ export function useTheme() {
501
+ const context = useContext(ThemeContext)
502
+ if (!context) {
503
+ throw new Error('useTheme must be used within ThemeProvider')
504
+ }
505
+ return context
506
+ }
507
+ ```
508
+
509
+ ### 优化 Context
510
+
511
+ ```typescript
512
+ import { createContext, useContext, useMemo, ReactNode } from 'react'
513
+
514
+ // 分离状态和更新函数
515
+ const StateContext = createContext<State | undefined>(undefined)
516
+ const DispatchContext = createContext<Dispatch | undefined>(undefined)
517
+
518
+ export function Provider({ children }: { children: ReactNode }) {
519
+ const [state, dispatch] = useReducer(reducer, initialState)
520
+
521
+ // 防止不必要的重渲染
522
+ const memoizedState = useMemo(() => state, [state])
523
+ const memoizedDispatch = useMemo(() => dispatch, [dispatch])
524
+
525
+ return (
526
+ <StateContext.Provider value={memoizedState}>
527
+ <DispatchContext.Provider value={memoizedDispatch}>
528
+ {children}
529
+ </DispatchContext.Provider>
530
+ </StateContext.Provider>
531
+ )
532
+ }
533
+ ```
534
+
535
+ ## 性能优化
536
+
537
+ ### Redux 选择器优化
538
+
539
+ ```typescript
540
+ import { createSelector } from '@reduxjs/toolkit'
541
+
542
+ // 基础选择器
543
+ const selectTodos = (state: RootState) => state.todos
544
+ const selectFilter = (state: RootState) => state.filter
545
+
546
+ // Memoized 选择器
547
+ export const selectFilteredTodos = createSelector(
548
+ [selectTodos, selectFilter],
549
+ (todos, filter) => {
550
+ switch (filter) {
551
+ case 'completed':
552
+ return todos.filter((t) => t.done)
553
+ case 'active':
554
+ return todos.filter((t) => !t.done)
555
+ default:
556
+ return todos
557
+ }
558
+ }
559
+ )
560
+ ```
561
+
562
+ ### Zustand 选择器
563
+
564
+ ```typescript
565
+ // 避免不必要的重渲染
566
+ function Component() {
567
+ // ❌ 整个 state 变化都会重渲染
568
+ const state = useStore()
569
+
570
+ // ✅ 只在 bears 变化时重渲染
571
+ const bears = useStore((state) => state.bears)
572
+
573
+ // ✅ 使用 shallow 比较
574
+ const { bears, increase } = useStore(
575
+ (state) => ({ bears: state.bears, increase: state.increase }),
576
+ shallow
577
+ )
578
+ }
579
+ ```
580
+
581
+ ### Jotai 优化
582
+
583
+ ```typescript
584
+ // 使用 selectAtom 避免不必要的重渲染
585
+ import { selectAtom } from 'jotai/utils'
586
+
587
+ const userAtom = atom({ name: 'John', age: 30 })
588
+ const nameAtom = selectAtom(userAtom, (user) => user.name)
589
+
590
+ function Component() {
591
+ // 只在 name 变化时重渲染
592
+ const name = useAtomValue(nameAtom)
593
+ }
594
+ ```
595
+
596
+ ## 最佳实践
597
+
598
+ ### 状态分层
599
+
600
+ ```
601
+ 全局状态 (Redux/Zustand)
602
+ ├─ 用户认证
603
+ ├─ 主题配置
604
+ └─ 全局通知
605
+
606
+ 服务器状态 (React Query/SWR)
607
+ ├─ API 数据
608
+ ├─ 缓存管理
609
+ └─ 乐观更新
610
+
611
+ 组件状态 (useState/useReducer)
612
+ ├─ 表单输入
613
+ ├─ UI 交互
614
+ └─ 临时数据
615
+ ```
616
+
617
+ ### 命名规范
618
+
619
+ ```typescript
620
+ // Redux
621
+ const userSlice = createSlice({ name: 'user', ... })
622
+ export const { setUser, clearUser } = userSlice.actions
623
+
624
+ // Zustand
625
+ export const useUserStore = create<UserStore>(...)
626
+
627
+ // Jotai
628
+ export const userAtom = atom<User | null>(null)
629
+ export const userNameAtom = atom((get) => get(userAtom)?.name)
630
+
631
+ // Recoil
632
+ export const userState = atom({ key: 'userState', ... })
633
+ export const userNameState = selector({ key: 'userNameState', ... })
634
+ ```
635
+
636
+ ### 错误处理
637
+
638
+ ```typescript
639
+ // Redux Toolkit
640
+ const userSlice = createSlice({
641
+ name: 'user',
642
+ initialState: {
643
+ data: null,
644
+ error: null as string | null,
645
+ loading: false,
646
+ },
647
+ extraReducers: (builder) => {
648
+ builder.addCase(fetchUser.rejected, (state, action) => {
649
+ state.error = action.error.message || 'Unknown error'
650
+ state.loading = false
651
+ })
652
+ },
653
+ })
654
+
655
+ // Zustand
656
+ export const useStore = create<Store>((set) => ({
657
+ error: null,
658
+ fetchData: async () => {
659
+ try {
660
+ const data = await api.fetch()
661
+ set({ data, error: null })
662
+ } catch (error) {
663
+ set({ error: error.message })
664
+ }
665
+ },
666
+ }))
667
+ ```
668
+
669
+ ## 工具清单
670
+
671
+ | 工具 | 用途 |
672
+ |------|------|
673
+ | Redux DevTools | 时间旅行调试 |
674
+ | Zustand DevTools | Zustand 状态调试 |
675
+ | Jotai DevTools | Atom 依赖可视化 |
676
+ | Recoil DevTools | Recoil 状态调试 |
677
+ | React Query DevTools | 服务器状态调试 |
678
+ | Immer | 不可变数据更新 |
679
+
680
+ ---