Files
lobe-chat/.cursor/rules/zustand-slice-organization.mdc
Arvin Xu 987b633336
Some checks failed
Upstream Sync / Sync latest commits from upstream repo (push) Has been cancelled
Release CI / Release (push) Has been cancelled
Test CI / test (push) Has been cancelled
Lighthouse Badger / LobeChat | Chat (push) Has been cancelled
Lighthouse Badger / LobeChat | Market (push) Has been cancelled
Issue Close Require / issue-check-inactive (push) Has been cancelled
Issue Close Require / issue-close-require (push) Has been cancelled
Daily i18n Update / update-i18n (push) Has been cancelled
📝 docs: add some cursor rules (#8338)
2025-07-04 19:07:16 +08:00

301 lines
8.3 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
description:
globs: src/store/**
alwaysApply: false
---
# LobeChat Zustand Store Slice 组织架构
本文档描述了 LobeChat 项目中 Zustand Store 的模块化 Slice 组织方式,展示如何通过分片架构管理复杂的应用状态。
## 顶层 Store 结构
LobeChat 的 `chat` store (`src/store/chat/`) 采用模块化的 slice 结构来组织状态和逻辑。
### 关键聚合文件
- `src/store/chat/initialState.ts`: 聚合所有 slice 的初始状态
- `src/store/chat/store.ts`: 定义顶层的 `ChatStore`,组合所有 slice 的 actions
- `src/store/chat/selectors.ts`: 统一导出所有 slice 的 selectors
- `src/store/chat/helpers.ts`: 提供聊天相关的辅助函数
### Store 聚合模式
```typescript
// src/store/chat/initialState.ts
import { ChatTopicState, initialTopicState } from './slices/topic/initialState';
import { ChatMessageState, initialMessageState } from './slices/message/initialState';
import { ChatAIChatState, initialAiChatState } from './slices/aiChat/initialState';
export type ChatStoreState = ChatTopicState &
ChatMessageState &
ChatAIChatState &
// ...其他 slice states
export const initialState: ChatStoreState = {
...initialMessageState,
...initialTopicState,
...initialAiChatState,
// ...其他 initial slice states
};
```
```typescript
// src/store/chat/store.ts
import { ChatMessageAction, chatMessage } from './slices/message/action';
import { ChatTopicAction, chatTopic } from './slices/topic/action';
import { ChatAIChatAction, chatAiChat } from './slices/aiChat/actions';
export interface ChatStoreAction
extends ChatMessageAction,
ChatTopicAction,
ChatAIChatAction,
// ...其他 slice actions
const createStore: StateCreator<ChatStore, [['zustand/devtools', never]]> = (...params) => ({
...initialState,
...chatMessage(...params),
...chatTopic(...params),
...chatAiChat(...params),
// ...其他 slice action creators
});
export const useChatStore = createWithEqualityFn<ChatStore>()(
subscribeWithSelector(devtools(createStore)),
shallow,
);
```
## 单个 Slice 的标准结构
每个 slice 位于 `src/store/chat/slices/[sliceName]/` 目录下:
```
src/store/chat/slices/
└── [sliceName]/ # 例如 message, topic, aiChat, builtinTool
├── action.ts # 定义 actions (或者是一个 actions/ 目录)
├── initialState.ts # 定义 state 结构和初始值
├── reducer.ts # (可选) 如果使用 reducer 模式
├── selectors.ts # 定义 selectors
└── index.ts # (可选) 重新导出模块内容
```
### 文件职责说明
1. `initialState.ts`:
- 定义 slice 的 TypeScript 状态接口
- 提供初始状态默认值
```typescript
// 典型的 initialState.ts 结构
export interface ChatTopicState {
activeTopicId?: string;
topicMaps: Record<string, ChatTopic[]>; // 核心数据结构
topicsInit: boolean;
topicLoadingIds: string[];
// ...其他状态字段
}
export const initialTopicState: ChatTopicState = {
activeTopicId: undefined,
topicMaps: {},
topicsInit: false,
topicLoadingIds: [],
// ...其他初始值
};
```
2. `reducer.ts` (复杂状态使用):
- 定义纯函数 reducer处理同步状态转换
- 使用 `immer` 确保不可变更新
```typescript
// 典型的 reducer.ts 结构
import { produce } from 'immer';
interface AddChatTopicAction {
type: 'addTopic';
value: CreateTopicParams & { id?: string };
}
interface UpdateChatTopicAction {
id: string;
type: 'updateTopic';
value: Partial<ChatTopic>;
}
export type ChatTopicDispatch = AddChatTopicAction | UpdateChatTopicAction;
export const topicReducer = (state: ChatTopic[] = [], payload: ChatTopicDispatch): ChatTopic[] => {
switch (payload.type) {
case 'addTopic': {
return produce(state, (draftState) => {
draftState.unshift({
...payload.value,
id: payload.value.id ?? Date.now().toString(),
createdAt: Date.now(),
});
});
}
case 'updateTopic': {
return produce(state, (draftState) => {
const index = draftState.findIndex((topic) => topic.id === payload.id);
if (index !== -1) {
draftState[index] = { ...draftState[index], ...payload.value };
}
});
}
default:
return state;
}
};
```
3. `selectors.ts`:
- 提供状态查询和计算函数
- 供 UI 组件使用的状态订阅接口
- 重要: 使用 `export const xxxSelectors` 模式聚合所有 selectors
```typescript
// 典型的 selectors.ts 结构
import { ChatStoreState } from '../../initialState';
const currentTopics = (s: ChatStoreState): ChatTopic[] | undefined =>
s.topicMaps[s.activeId];
const currentActiveTopic = (s: ChatStoreState): ChatTopic | undefined => {
return currentTopics(s)?.find((topic) => topic.id === s.activeTopicId);
};
const getTopicById = (id: string) => (s: ChatStoreState): ChatTopic | undefined =>
currentTopics(s)?.find((topic) => topic.id === id);
// 核心模式:使用 xxxSelectors 聚合导出
export const topicSelectors = {
currentActiveTopic,
currentTopics,
getTopicById,
// ...其他 selectors
};
```
## 特殊 Slice 组织模式
### 复杂 Actions 的子目录结构 (aiChat Slice)
当 slice 的 actions 过于复杂时,可以拆分到子目录:
```
src/store/chat/slices/aiChat/
├── actions/
│ ├── generateAIChat.ts # AI 对话生成
│ ├── rag.ts # RAG 检索增强生成
│ ├── memory.ts # 对话记忆管理
│ └── index.ts # 聚合所有 actions
├── initialState.ts
├── selectors.ts
└── index.ts
```
参考:`src/store/chat/slices/aiChat/actions/`
### 工具类 Slice (builtinTool)
管理多种内置工具的状态:
```
src/store/chat/slices/builtinTool/
├── actions/
│ ├── dalle.ts # DALL-E 图像生成
│ ├── search.ts # 搜索功能
│ ├── localFile.ts # 本地文件操作
│ └── index.ts
├── initialState.ts
├── selectors.ts
└── index.ts
```
参考:`src/store/chat/slices/builtinTool/`
## 状态设计模式
### 1. Map 结构用于关联数据
```typescript
// 以 sessionId 为 key管理多个会话的数据
topicMaps: Record<string, ChatTopic[]>
messagesMap: Record<string, ChatMessage[]>
```
### 2. 数组用于加载状态管理
```typescript
// 管理多个并发操作的加载状态
messageLoadingIds: string[]
topicLoadingIds: string[]
chatLoadingIds: string[]
```
### 3. 可选字段用于当前活动项
```typescript
// 当前激活的实体 ID
activeId: string
activeTopicId?: string
activeThreadId?: string
```
## Slice 集成到顶层 Store
### 1. 状态聚合
```typescript
// 在 initialState.ts 中
export type ChatStoreState = ChatTopicState &
ChatMessageState &
ChatAIChatState &
// ...其他 slice states
```
### 2. Action 接口聚合
```typescript
// 在 store.ts 中
export interface ChatStoreAction
extends ChatMessageAction,
ChatTopicAction,
ChatAIChatAction,
// ...其他 slice actions
```
### 3. Selector 统一导出
```typescript
// 在 selectors.ts 中 - 统一聚合 selectors
export { chatSelectors } from './slices/message/selectors';
export { topicSelectors } from './slices/topic/selectors';
export { aiChatSelectors } from './slices/aiChat/selectors';
// 每个 slice 的 selectors.ts 都使用 xxxSelectors 模式:
// export const chatSelectors = { ... }
// export const topicSelectors = { ... }
// export const aiChatSelectors = { ... }
```
## 最佳实践
1. Slice 划分原则:
- 按功能领域划分message, topic, aiChat 等)
- 每个 slice 管理相关的状态和操作
- 避免 slice 之间的强耦合
2. 文件命名规范:
- 使用小驼峰命名 slice 目录
- 文件名使用一致的模式action.ts, selectors.ts 等)
- 复杂 actions 时使用 actions/ 子目录
3. 状态结构设计:
- 扁平化的状态结构,避免深层嵌套
- 使用 Map 结构管理列表数据
- 分离加载状态和业务数据
4. 类型安全:
- 为每个 slice 定义清晰的 TypeScript 接口
- 使用 Zustand 的 StateCreator 确保类型一致性
- 在顶层聚合时保持类型安全
这种模块化的 slice 组织方式使得大型应用的状态管理变得清晰、可维护,并且易于扩展。