使用next-i18next时Next.js页面布局重复渲染问题求助
Next-i18next 布局重复渲染问题解决方案
问题描述
使用next-i18next时,已按规范完成实现,但带有getStaticProps的页面布局组件总是重复渲染,需要阻止该现象。我创建了withStaticTranslations.ts工具函数并在页面中使用,但该翻译逻辑无法在_app.tsx或全局Layout组件中生效。
现有代码
withStaticTranslations.ts
import { GetStaticProps, GetStaticPropsContext, GetStaticPropsResult } from 'next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; type WithStaticTranslationsOptions = { namespaces?: string[]; }; export const withStaticTranslations = ( getStaticPropsFunc?: GetStaticProps, { namespaces = ['common'] }: WithStaticTranslationsOptions = {} ): GetStaticProps => { return async (context: GetStaticPropsContext): Promise<GetStaticPropsResult<any>> => { const translations = await serverSideTranslations(context.locale || 'en', namespaces); if (getStaticPropsFunc) { const pageStaticProps = await getStaticPropsFunc(context); if ('props' in pageStaticProps) { return { props: { ...translations, ...pageStaticProps.props, }, }; } return pageStaticProps; } return { props: translations }; }; };
页面使用示例
import type { NextPage } from 'next' import React from "react"; import ImageSlider from "@/components/Organisms/ImageSlider"; import { withStaticTranslations } from '@/composables/withStaticTranslations'; import { GetStaticProps } from 'next'; const Home: NextPage = () => { return ( <> <ImageSlider /> </> ) } export const getStaticProps: GetStaticProps = withStaticTranslations(); export default Home;
解决方案
方案一:用React.memo缓存Layout组件(最快见效)
布局重复渲染的核心原因是:每次页面切换时,serverSideTranslations返回的翻译对象会被视为新的props,触发Layout组件重渲染。用React.memo对Layout组件做浅比较缓存,可直接阻止无意义的重渲染。
修改Layout组件:
import React from 'react'; import { useTranslation } from 'next-i18next'; const Layout = ({ children }: { children: React.ReactNode }) => { const { t } = useTranslation('common'); return ( <div> <header>{t('header.title')}</header> {children} </div> ); }; // 仅当props真正变化时才重渲染组件 export default React.memo(Layout);
方案二:在_app.tsx中处理公共翻译,避免重复加载(性能优化)
若担心在_app.tsx中加载所有翻译影响性能,可以仅在_app.tsx中加载公共命名空间(如common),页面级命名空间由各自的getStaticProps单独加载,既避免布局重复渲染,又不会一次性加载所有翻译资源。
- 修改
_app.tsx添加公共翻译逻辑:
import type { AppProps } from 'next/app'; import { appWithTranslation } from 'next-i18next'; import Layout from '@/components/Layout'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; function MyApp({ Component, pageProps }: AppProps) { return ( <Layout> <Component {...pageProps} /> </Layout> ); } // 在_app中加载公共命名空间,所有页面自动继承这些翻译props export async function getStaticProps(context: any) { return { props: await serverSideTranslations(context.locale || 'en', ['common']), }; } export default appWithTranslation(MyApp);
- 修改
withStaticTranslations避免重复加载公共命名空间:
export const withStaticTranslations = ( getStaticPropsFunc?: GetStaticProps, { namespaces = [] }: WithStaticTranslationsOptions = {} // 默认不加载common,由_app提供 ): GetStaticProps => { return async (context: GetStaticPropsContext): Promise<GetStaticPropsResult<any>> => { // 仅加载页面专属的命名空间 const translations = namespaces.length > 0 ? await serverSideTranslations(context.locale || 'en', namespaces) : {}; if (getStaticPropsFunc) { const pageStaticProps = await getStaticPropsFunc(context); if ('props' in pageStaticProps) { return { props: { ...translations, ...pageStaticProps.props, }, }; } return pageStaticProps; } return { props: translations }; }; };
- 页面使用时传入专属命名空间:
export const getStaticProps: GetStaticProps = withStaticTranslations(undefined, { namespaces: ['home'] // 仅加载home页面的专属翻译 });
方案三:稳定翻译props引用(进阶优化)
若上述方案仍有问题,可通过稳定翻译对象引用进一步优化:在withStaticTranslations中合并props时,用Object.freeze锁定对象,确保相同翻译内容不会生成新的引用。
修改withStaticTranslations的props合并逻辑:
if ('props' in pageStaticProps) { const mergedProps = { ...pageStaticProps.props, ...translations, }; // 冻结对象避免后续修改导致引用变化 Object.freeze(mergedProps); return { props: mergedProps, }; }
关键说明
withStaticTranslations无法直接在Layout组件中生效,因为getStaticProps是页面级生命周期函数,组件本身不能直接使用,必须通过页面或_app.tsx传递props。- 优先用方案一快速解决重渲染问题,再结合方案二优化性能,避免不必要的翻译资源加载。
内容的提问来源于stack exchange,提问作者Victus
相关产品推荐
相关产品推荐

