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

Next.js仪表盘页面路由如何实现最小化重渲染?

问题

我当前的Dashboard组件结构如下:

export default function Dashboard({ children }) {
return (
    <>
        <DashboardLayout menu={menu}>
            <DashboardPage variant="cards" flow="one-one">
                {children}
            </DashboardPage>
        </DashboardLayout>
    </>
)
}

这个仪表盘左侧有侧边栏菜单用于切换不同DashboardPage,菜单、侧边栏、页脚等组件是各页面共享的,我希望避免这些组件重复渲染。但使用Next.js原生<Link>组件切换页面时,所有组件都会重渲染,目前我想到的替代方案是用useState或useReducer钩子控制页面渲染:

export default function Dashboard() {
const [state, dispatch] = useReducer();

return (
    <>
        <DashboardLayout menu={menu}>
            <DashboardPage variant="cards" flow="one-one">
                {state == 'page1' && <DashboardPage1 />}
                {state == 'page2' && <DashboardPage2 />}
                {state == 'page3' && <DashboardPage3 />}
                ...
                {state == 'pageN' && <DashboardPageN />}
            </DashboardPage>
        </DashboardLayout>
    </>
)
}

请问是否可以使用路由(如useRoute钩子)实现页面切换,同时避免DashboardLayout等无需重渲染的组件重复渲染?目前切换页面时,DashboardLayout内的控制台日志会打印4次。

解决方案

当然可以通过路由实现页面切换同时避免共享组件重渲染,核心思路是让DashboardLayout这类共享组件脱离路由切换的层级,只让页面内容部分响应路由变化,同时结合React的性能优化手段:

1. 利用Next.js嵌套路由结构(推荐)

不管你用的是App Router还是Pages Router,都可以通过嵌套路由让共享组件作为父路由的一部分,子路由仅渲染页面内容:

针对App Router(Next.js 13+)

  • 新建路由结构:
    app/
      dashboard/
        layout.jsx  # 放置DashboardLayout
        page1/
          page.jsx
        page2/
          page.jsx
        page3/
          page.jsx
    
  • dashboard/layout.jsx内容:
    export default function DashboardLayout({ children }) {
      // 这里的内容仅首次加载时渲染,切换子路由不会重渲染
      return (
        <DashboardLayout menu={menu}>
          <DashboardPage variant="cards" flow="one-one">
            {children}
          </DashboardPage>
        </DashboardLayout>
      )
    }
    
  • 子路由页面(如page1/page.jsx)直接导出页面组件:
    export default function Page1() {
      return <DashboardPage1 />
    }
    
    切换/dashboard/page1和/dashboard/page2时,仅children部分(子页面组件)会重新渲染,DashboardLayout不会重复渲染。

针对Pages Router

  • 新建pages/dashboard/[pageSlug].js作为动态路由,同时在pages/dashboard/_app.js里放置共享布局:
    • pages/dashboard/_app.js:
      import DashboardLayout from '../../../components/DashboardLayout'
      import DashboardPage from '../../../components/DashboardPage'
      
      export default function DashboardApp({ Component, pageProps }) {
        return (
          <DashboardLayout menu={menu}>
            <DashboardPage variant="cards" flow="one-one">
              <Component {...pageProps} />
            </DashboardPage>
          </DashboardLayout>
        )
      }
      
    • pages/dashboard/[pageSlug].js:
      export default function DashboardDynamicPage({ pageSlug }) {
        switch(pageSlug) {
          case 'page1':
            return <DashboardPage1 />
          case 'page2':
            return <DashboardPage2 />
          // 其他页面分支
          default:
            return <NotFound />
        }
      }
      
      export async function getStaticPaths() {
        return {
          paths: [{ params: { pageSlug: 'page1' } }, { params: { pageSlug: 'page2' } }],
          fallback: false
        }
      }
      
      export async function getStaticProps({ params }) {
        return { props: { pageSlug: params.pageSlug } }
      }
      
    这种方式下,DashboardLayout作为_app.js里的组件,仅在进入dashboard路由组时渲染一次,切换子页面不会重渲染。

2. 优化组件本身的渲染逻辑

如果不想调整路由结构,也可以通过以下方式减少DashboardLayout的重渲染:

  • 用React.memo包裹DashboardLayout,确保仅当menu等props变化时才重渲染:
    import { memo } from 'react'
    
    const DashboardLayout = memo(({ menu, children }) => {
      // 组件内容
    })
    
    注意如果menu是引用类型(对象、数组),要用useMemo缓存保证引用稳定:
    const memoizedMenu = useMemo(() => menu, [/* 依赖项 */])
    
  • 检查DashboardLayout内部是否有不必要的状态更新或依赖项错误,比如useEffect依赖项数组不正确导致重复执行日志打印。

3. 结合路由状态控制渲染

如果你想继续用路由钩子(App Router的usePathname或Pages Router的useRouter)控制页面渲染,也能实现无重渲染效果:

import { usePathname } from 'next/navigation' // App Router
// 或 import { useRouter } from 'next/router' // Pages Router

export default function Dashboard() {
  const pathname = usePathname()
  // Pages Router写法:const { pathname } = useRouter()

  return (
    <>
      <DashboardLayout menu={menu}>
        <DashboardPage variant="cards" flow="one-one">
          {pathname.includes('page1') && <DashboardPage1 />}
          {pathname.includes('page2') && <DashboardPage2 />}
          {/* 其他页面分支 */}
        </DashboardPage>
      </DashboardLayout>
    </>
  )
}

配合React.memo包裹DashboardLayout,只要menu的引用稳定,DashboardLayout就不会因路由切换而重渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 02:50:28