✍️ 大型项目实战经验总结
🎯 目标:不是教你用 Redux,而是教你如何架好一个能撑 3 年不塌的 Redux 状态系统
🧠 关键词:模块化、可组合、解耦化、Redux Toolkit、异步策略、团队协作
💥 初学 Redux 时的典型错误架构
- 所有 action 放一个文件里:
actions.js - 所有 reducer 混在一起:
reducer.js - 状态结构像「垃圾堆」一样扁平混乱
- 异步写死在组件里,用大量 useEffect + dispatch
- 命名随缘、action type 到处 hardcode
结果就是:
- 状态不能复用;
- 模块不能解耦;
- 异步处理乱七八糟;
- onboarding 一个新人,他三天都看不懂状态流程。
✅ 最佳实践一:状态架构的基本原则
Redux 架构设计的第一步:不要“按文件功能”分,而是“按领域模块”分。
🧱 推荐的目录结构(Domain-Centric):
src/
├── store/
│ ├── index.ts // 创建和导出 store
│ └── slices/
│ ├── userSlice.ts // 用户模块
│ ├── postSlice.ts // 帖子模块
│ └── commentSlice.ts
├── features/
│ ├── user/
│ │ └── UserProfile.tsx
│ └── post/
│ └── PostList.tsx
每个 slice:
- 只负责自己的状态;
- 导出自己的 actions;
- 提供 selector 封装;
- 和 UI 解耦(不直接操作组件)。
📦 最佳实践二:使用 Redux Toolkit 统一规范
我们在内部项目中不再手写 action type 和 reducer,而是统一使用 createSlice 和 createAsyncThunk。
🚀 createSlice 示例:
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment(state) {
state.value += 1
},
set(state, action: PayloadAction<number>) {
state.value = action.payload
}
}
})
🎯 好处:
- 自动生成 action creator;
- 支持 Immer 写法(写“可变”代码,执行“不可变”逻辑);
- 和 TS 类型推导天然结合;
- 默认内聚,代码维护成本低。
🔄 最佳实践三:异步操作用 createAsyncThunk 管理
export const fetchUser = createAsyncThunk(
'user/fetch',
async (id: string) => {
const res = await api.getUser(id)
return res.data
}
)
然后在 extraReducers 中处理 loading / success / error:
extraReducers: builder => {
builder
.addCase(fetchUser.pending, state => {
state.loading = true
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.profile = action.payload
state.loading = false
})
}
你再也不用手写 loading、error 管理逻辑了,Toolkit 自动分发 3 个 action:
user/fetch/pending
user/fetch/fulfilled
user/fetch/rejected
🧠 最佳实践四:Selector 是状态系统的“接口层”
直接在组件中写 state.user.data.xxx 会让组件依赖状态结构细节,耦合性高。
正确做法:
// userSlice.ts
export const selectUserName = (state: RootState) => state.user.name
组件中只使用:
const name = useSelector(selectUserName)
Selector 是状态的“公开 API”,防止内部结构泄漏,未来 refactor 更安心。
🔒 最佳实践五:store 解耦,支持动态注入 reducer(微前端/插件场景)
Redux 支持动态注册 reducer:
store.injectReducer('chat', chatReducer)
适用于:
- 微前端应用按需加载;
- 页面级模块异步挂载;
- 插件系统动态注册状态域。
Toolkit 也支持动态组合 reducer:
const rootReducer = combineReducers({
user: userReducer,
...(extraReducers || {})
})
⚙️ 最佳实践六:统一中间件策略处理日志、权限、埋点
不要在组件里写这些逻辑,推荐中间件封装:
const loggerMiddleware = store => next => action => {
console.log('[Action]', action.type)
return next(action)
}
组合使用:
configureStore({
reducer,
middleware: getDefaultMiddleware().concat(loggerMiddleware)
})
统一处理:
- 埋点;
- 权限校验;
- 接口限频;
- 请求重试;
- 错误拦截。
📐 最佳实践七:团队协作建议(TS 项目)
- 所有 slice 都导出自己的
actions+selectors - 禁止跨模块 dispatch 其他模块 action
- 定义统一的 RootState 和 AppDispatch 类型:
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
并封装 hook:
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector = useSelector.withTypes<RootState>()
📊 总结:Redux 不是写法问题,而是架构问题
你不用 Toolkit 时,Redux 像 C 语言 —— 灵活但容易踩雷; 用了 Toolkit + 拆分最佳实践,Redux 就像 Rust —— 安全、有约束、强工程力。
优秀的 Redux 架构应满足:
| 维度 | 表现 |
|---|---|
| 可维护性 | slice 明确,低耦合 |
| 可扩展性 | 支持动态注入,异步加载 |
| 可测试性 | reducer/selector 易单测 |
| 可协作性 | action/selector 可文档化 |
| 性能表现 | connect/useSelector + 缓存 |
⏭️ 下一篇预告
我们将进入实战写代码阶段,下一篇: 第7篇:《Redux Toolkit 源码解构与工程设计精髓》
你将看到:createSlice、createAsyncThunk 背后的自动代码生成机制、Immer 集成方式、TS 类型推导策略——一切都不再神秘。