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
相关产品推荐
相关产品推荐

