18.3 状态管理(Redux 或 Zustand)
18.3 状态管理(Redux 或 Zustand)
现代SPA应用的状态管理是核心架构难题,本节将对比讲解Redux和Zustand两种主流方案,并提供完整的实现模式。
Redux 现代化实践
1. 基础Store配置
// store.js
import { configureStore } from '@reduxjs/toolkit';
import {
persistStore,
persistReducer,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER
} from 'redux-persist';
import storage from 'redux-persist/lib/storage';
const persistConfig = {
key: 'root',
version: 1,
storage,
blacklist: ['tempData']
};
const rootReducer = combineReducers({
user: userReducer,
cart: cartReducer
});
const persistedReducer = persistReducer(persistConfig, rootReducer);
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER]
}
}),
devTools: process.env.NODE_ENV !== 'production'
});
export const persistor = persistStore(store);
2. 现代Redux切片模式
// features/user/userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import authAPI from '../../api/auth';
export const fetchUser = createAsyncThunk(
'user/fetch',
async (userId, { rejectWithValue }) => {
try {
return await authAPI.getUser(userId);
} catch (err) {
return rejectWithValue(err.response.data);
}
}
);
const userSlice = createSlice({
name: 'user',
initialState: {
data: null,
status: 'idle',
error: null
},
reducers: {
logout: (state) => {
state.data = null;
}
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.status = 'succeeded';
state.data = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
state.status = 'failed';
state.error = action.payload;
});
}
});
export const { logout } = userSlice.actions;
export default userSlice.reducer;
3. 性能优化方案
// 使用memoized selectors
import { createSelector } from '@reduxjs/toolkit';
const selectUser = (state) => state.user;
export const selectUserPermissions = createSelector(
[selectUser],
(user) => user.data?.permissions ?? []
);
// 组件中使用
import { shallowEqual, useSelector } from 'react-redux';
const Component = () => {
// 浅比较避免不必要的重渲染
const permissions = useSelector(selectUserPermissions, shallowEqual);
// ...
};
Zustand 轻量级方案
1. 基础Store创建
// store/useStore.js
import create from 'zustand';
import { persist } from 'zustand/middleware';
const useStore = create(
persist(
(set, get) => ({
user: null,
cart: [],
login: async (credentials) => {
const user = await authAPI.login(credentials);
set({ user });
},
addToCart: (product) =>
set((state) => ({
cart: [...state.cart, product]
})),
getCartTotal: () =>
get().cart.reduce((total, item) => total + item.price, 0)
}),
{
name: 'app-storage',
getStorage: () => localStorage,
partialize: (state) =>
Object.fromEntries(
Object.entries(state).filter(
([key]) => !['tempData'].includes(key)
)
)
}
)
);
// 派生状态选择器
export const useUser = () => useStore(state => state.user);
export const useCart = () => useStore(state => state.cart);
export const useCartTotal = () => useStore(state => state.getCartTotal());
2. 异步操作处理
const useStore = create((set) => ({
posts: [],
status: 'idle',
fetchPosts: async () => {
set({ status: 'loading' });
try {
const posts = await api.getPosts();
set({ posts, status: 'success' });
} catch (err) {
set({ status: 'error' });
console.error(err);
}
}
}));
// 组件中使用
const Component = () => {
const { posts, status, fetchPosts } = useStore();
useEffect(() => {
fetchPosts();
}, []);
if (status === 'loading') return <Spinner />;
// ...
};
3. 中间件集成
import { devtools, subscribeWithSelector } from 'zustand/middleware';
const useStore = create(
subscribeWithSelector(
devtools(
(set) => ({
// ...store逻辑
}),
{ name: 'AppStore' }
)
)
);
// 状态变化订阅
useStore.subscribe(
(state) => state.user,
(user) => {
analytics.track('UserChanged', user);
},
{ fireImmediately: true }
);
对比决策指南
| 特性 | Redux Toolkit | Zustand |
|---|---|---|
| 学习曲线 | 中等(需要理解概念较多) | 简单(API极少) |
| 样板代码 | 中等(使用RTK后减少) | 极少 |
| 性能 | 需要手动优化(selector) | 自动优化 |
| 中间件系统 | 强大 | 灵活但生态较小 |
| 调试工具 | Redux DevTools 强大 | 基础支持 |
| 适用场景 | 大型复杂应用 | 中小型应用/局部状态 |
| 包大小 | ~7KB (RTK + React-Redux) | ~1KB |
| SSR支持 | 完善 | 需要手动处理 |
混合使用模式
1. 全局+局部状态组合
// 全局使用Redux
import { Provider } from 'react-redux';
function App() {
return (
<Provider store={store}>
<MainLayout />
</Provider>
);
}
// 组件内部使用Zustand
function CartButton() {
const cartItems = useCartStore(state => state.items);
// ...
}
2. Redux迁移兼容方案
// 创建一个兼容Redux API的Zustand store
import { createStore } from 'zustand';
const reduxLikeStore = createStore((set) => ({
...reducer(undefined, { type: '@@INIT' }),
dispatch: (action) =>
set((state) => reducer(state, action))
}));
// 旧代码可以继续使用store.dispatch()
reduxLikeStore.dispatch({ type: 'INCREMENT' });
高级模式实践
1. 状态分形(Fractal State)
// 创建可组合的zustand stores
const createCounterStore = (initialCount = 0) =>
create(set => ({
count: initialCount,
increment: () => set(state => ({ count: state.count + 1 })),
decrement: () => set(state => ({ count: state.count - 1 }))
}));
// 在父store中嵌套
const useAppStore = create(set => ({
users: createUserStore(),
counter1: createCounterStore(0),
counter2: createCounterStore(10)
}));
2. 时间旅行调试
// 基于zustand实现
const useStore = create(
temporal(
(set) => ({
// ...store逻辑
}),
{
limit: 10, // 历史记录数量
wrapTemporal: true // 循环记录
}
)
);
// 在组件中使用
const { undo, rewind } = useTemporal(useStore);
3. 状态持久化策略
// 混合持久化方案
const useStore = create(
persist(
(set) => ({
// 敏感数据存sessionStorage
auth: null,
// 用户偏好存localStorage
preferences: {},
// 临时数据不持久化
tempData: {}
}),
{
name: 'app-store',
storage: {
getItem: (name) => {
const str = localStorage.getItem(name);
const { state } = JSON.parse(str);
return {
state: {
...state,
auth: sessionStorage.getItem('auth')
? JSON.parse(sessionStorage.getItem('auth'))
: state.auth
}
}
},
setItem: (name, value) => {
const { state } = JSON.parse(value);
sessionStorage.setItem('auth', JSON.stringify(state.auth));
localStorage.setItem(name, JSON.stringify({
state: {
...state,
auth: null
}
}));
},
removeItem: (name) => {
sessionStorage.removeItem('auth');
localStorage.removeItem(name);
}
}
}
)
);
性能优化技巧
- 细粒度订阅:
// 只订阅需要的部分状态
const username = useStore(
state => state.user.name,
(prev, next) => prev === next // 自定义相等比较
);
- 批量更新:
// Zustand自动批处理
useStore.setState({
user: updatedUser,
lastUpdated: Date.now()
});
// Redux需要手动批处理
import { batch } from 'react-redux';
batch(() => {
dispatch(updateUser(user));
dispatch(updateTimestamp());
});
- Web Worker集成:
// 将复杂计算移到Worker
const useStore = create((set) => ({
data: null,
worker: new Worker('./worker.js'),
initWorker: () => {
const worker = new Worker('./worker.js');
worker.onmessage = (e) =>
set({ data: e.data });
set({ worker });
}
}));
迁移策略建议
-
从Redux迁移到Zustand:
- 先替换非核心模块的状态
- 使用Redux兼容层逐步迁移
- 最后移除React-Redux依赖
-
从Context迁移:
- 识别性能瓶颈组件
- 将频繁更新的状态移至Zustand
- 保留静态配置在Context中
通过以上方案,你可以根据项目需求选择最适合的状态管理策略。接下来我们将进入实时聊天应用的实战开发。
#前端开发
分享于 2025-03-25