如何在 SSR 中实现 Redux/Vuex 的状态同步?

探讨在服务端渲染(SSR)环境下,如何利用状态管理库如 Redux 或 Vuex 实现前后端状态的高效同步。文章详细介绍了从服务端初始化到客户端水合的完整流程,并对比了两者的解决方案。

服务端渲染 困难 SSR 状态管理 Redux

使用 Redux 或 Vuex 在 SSR 中解决状态同步问题需遵循以下流程:

  1. 服务端状态初始化
    在服务端渲染时创建新的状态管理实例并注入初始数据。
    Vuex 示例
    // 创建新的 Vuex store 实例
    export default () => new Vuex.Store({ state, mutations, actions });
    

    Redux 示例

    // 创建新的 Redux store
    export default () => createStore(reducer);
    
  2. 数据预取与填充
    在服务端执行异步逻辑(如请求 API)并提交状态变更。
    Vuex 方式:通过 actions 异步获取数据后 commit mutation:
    actions: {
      async fetchData({ commit }) {
        const data = await api.get();
        commit('SET_DATA', data); // 同步更新状态
      }
    }
    

    Redux 方式:使用中间件(如 redux-thunk)异步调度 action:

    const fetchData = () => async dispatch => {
      const data = await api.get();
      dispatch({ type: 'SET_DATA', payload: data }); 
    };
    
  3. 状态序列化与注入 HTML
    服务端将完整状态序列化后嵌入 HTML:
    <script>
      window.__INITIAL_STATE__ = ${JSON.stringify(store.getState())};
    </script>
    
  4. 客户端状态水合 (Hydration)
    客户端通过初始状态恢复 store,实现双向同步:
    Vuex 实现
    // 初始化 Vuex 时使用预填状态
    new Vuex.Store({ state: window.__INITIAL_STATE__ });
    

    Redux 实现

    // 创建 Redux store 时预加载状态
    createStore(reducer, window.__INITIAL_STATE__);
    
  5. 避免客户端重复请求与水合失败
    • 客户端检测机制:仅在未水合状态时执行数据请求;
    • 序列化校验:避免类型丢失,如 Date 类型需特殊处理;
    • 状态快照兼容性:确保服务端与客户端初始化逻辑一致,防止 hydration 错误。

方案对比:

问题点 Vuex 解决方案 Redux 解决方案
异步数据预取 Action 支持异步,同步 Commit 依赖中间件实现异步 Action
序列化风险 依赖 Proxy 无需处理序列化 JSON.stringify 需处理循环引用/类型
状态脱水 通过 __INITIAL_STATE__ 注入 preloadedState 初始化 store