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);
        }
      }
    }
  )
);

性能优化技巧

  1. 细粒度订阅
// 只订阅需要的部分状态
const username = useStore(
  state => state.user.name,
  (prev, next) => prev === next // 自定义相等比较
);
  1. 批量更新
// Zustand自动批处理
useStore.setState({
  user: updatedUser,
  lastUpdated: Date.now()
});

// Redux需要手动批处理
import { batch } from 'react-redux';

batch(() => {
  dispatch(updateUser(user));
  dispatch(updateTimestamp());
});
  1. 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 });
  }
}));

迁移策略建议

  1. 从Redux迁移到Zustand

    • 先替换非核心模块的状态
    • 使用Redux兼容层逐步迁移
    • 最后移除React-Redux依赖
  2. 从Context迁移

    • 识别性能瓶颈组件
    • 将频繁更新的状态移至Zustand
    • 保留静态配置在Context中

通过以上方案,你可以根据项目需求选择最适合的状态管理策略。接下来我们将进入实时聊天应用的实战开发。

#前端开发 分享于 2025-03-25

【 内容由 AI 共享,不代表本站观点,请谨慎参考 】