You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中Redux与RTKQ结合代码分割的正确实现方案

在Next.js中结合RTK/RTKQ实现Redux代码分割的可行方案及性能分析

一、可行的Redux代码分割实现方案

要解决全局Store竞态和RTKQ上下文访问的问题,核心是让每个请求/客户端会话拥有独立的Store实例,并在实例上挂载动态注入方法,而非操作全局Store。

1. 修改Store工厂函数,为每个实例添加动态注入能力

不再创建全局Store,而是在makeStore中为每个新Store实例单独添加injectDynamicReducer方法,同时传递请求上下文给RTKQ:

import { configureStore, combineReducers } from '@reduxjs/toolkit';
import { rtkqApi } from './features/apiSlice';
import globals from './features/globalsSlice';

export const makeStore = (ctx) => {
  // 每个Store实例维护自己的动态reducer集合
  const dynamicReducers = {};

  const store = configureStore({
    reducer: combineReducers({
      globals,
      [rtkqApi.reducerPath]: rtkqApi.reducer,
      ...dynamicReducers
    }),
    middleware: (getDefaultMiddleware) =>
      getDefaultMiddleware({
        thunk: {
          extraArgument: ctx, // 传递请求上下文给thunk
        },
      }).concat(rtkqApi.middleware),
  });

  // 为当前Store实例添加动态注入方法
  store.injectDynamicReducer = (name, reducer) => {
    if (dynamicReducers[name]) return; // 避免重复注入
    dynamicReducers[name] = reducer;
    // 更新根reducer
    store.replaceReducer(
      combineReducers({
        globals,
        [rtkqApi.reducerPath]: rtkqApi.reducer,
        ...dynamicReducers
      })
    );
  };

  return store;
};

export const wrapper = createWrapper(makeStore);

2. 在页面中按需注入Reducer(服务端+客户端)

需要在服务端的getServerSideProps和客户端的组件挂载阶段分别注入对应的切片,确保服务端渲染和客户端路由跳转都能正常加载状态:

import { useEffect } from 'react';
import { useSelector } from 'react-redux';
import { wrapper } from '../store';
import somePageSlice, { selectSomeData } from '../features/somePageSlice';

const SomePage = () => {
  const someData = useSelector(selectSomeData);

  // 客户端路由跳转时注入Reducer(防止首次渲染找不到state分支)
  useEffect(() => {
    wrapper.store.injectDynamicReducer('somePageSlice', somePageSlice.reducer);
  }, []);

  return (
    <>
      <title>Some Page</title>
      <div>{someData}</div>
    </>
  );
};

export const getServerSideProps = wrapper.getServerSideProps((store) => async (context) => {
  // 服务端注入Reducer
  store.injectDynamicReducer('somePageSlice', somePageSlice.reducer);
  // 可选:触发异步操作(如RTKQ查询或切片的thunk)
  await store.dispatch(somePageSlice.actions.fetchInitialData());

  return { props: {} };
});

export default SomePage;

3. RTKQ的代码分割优化

RTKQ本身支持通过懒加载Endpoint实现代码分割,无需额外配置:

  • 先创建空的API切片:
// features/apiSlice.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  endpoints: () => ({}), // 初始为空
});
  • 为特定页面创建独立的Endpoint文件:
// features/somePageApi.js
import { api } from './apiSlice';

export const somePageApi = api.injectEndpoints({
  endpoints: (build) => ({
    getSomePageData: build.query({ query: () => '/some-page-data' }),
  }),
});

// 导出自动生成的hooks
export const { useGetSomePageDataQuery } = somePageApi;
  • 在页面中仅导入该页面需要的Endpoint,Next.js会自动将这部分代码打包到页面专属的chunk中,不会混入主bundle。

二、不做代码分割的性能影响

如果将所有Redux切片和RTKQ Endpoint都全局注册到Store中,会带来以下影响:

  • Bundle体积增大:所有切片的reducer逻辑、action creators、selectors,以及RTKQ的所有Endpoint代码都会被打包进主bundle(或共享chunk),即使页面从未使用这些代码。这会直接延长首屏加载时间,影响LCP、FCP等核心性能指标。
  • 无树摇优化空间:由于Redux切片是全局导入并注册到根reducer的,Webpack/Next.js的树摇无法识别未使用的切片,会将所有相关代码保留在bundle中,而非仅保留初始状态。
  • 不必要的内存占用:客户端会加载所有切片的代码,即使用户从未访问对应页面,浪费浏览器内存。

总结

通过为每个Store实例添加动态注入能力,配合服务端和客户端的按需注入,完全可以在Next.js中实现RTK/RTKQ的代码分割。Redux官方文档提到的replaceReducer确实支持代码分割场景,并非仅用于热重载。如果项目切片较多,代码分割能有效优化首屏性能,建议优先实现。

内容的提问来源于stack exchange,提问作者Koy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 01:45:36