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

Next.js App Router根布局为何出现重渲染?

问题分析与解决:Next.js App Router 中 Navbar 重渲染及字体闪烁问题

问题背景

在 Next.js(App Router)中使用根布局渲染 Navbar 和子内容,官方文档明确说明:

布局是多页面间共享的UI,导航时布局会保留状态、保持交互且不会重渲染,布局也可嵌套。

但实际切换页面时,Navbar 控制台打印出"navbar rendering...",同时 Google 字体权重会短暂重置后恢复,与文档描述不符。

相关代码

根布局(app/layout.tsx)

import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { Russo_One } from "next/font/google";
import Navbar from "./_components/Navbar";

export const russo = Russo_One({ weight: "400", subsets: ["latin"] });

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Sportly",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Navbar />
        {children}
      </body>
    </html>
  );
}

Navbar 组件(app/_components/Navbar.tsx)

import { Link, Typography, Box } from "@mui/material";
import { russo } from "../layout";

export default function Navbar() {
  console.log("navbar rendering...");
  return (
    <nav className="/*STYLES*/">
        <h1 style={{fontWeight: "400" /* STYLES*/}} className={russo.className}>
          SPORTLY
        </h1>
    </nav>
  );
}

原因分析

  1. Navbar 重渲染的根源
    根布局本身不会重渲染,但 Navbar 作为根布局的子组件,直接从根布局模块导入了 russo 字体对象。Next.js 的 next/font/google 在开发模式下返回的字体对象可能存在不稳定的引用关系,导致 React 认为 Navbar 的依赖项发生变化,从而触发不必要的重渲染。

  2. 字体权重闪烁的原因
    开发模式下字体加载存在延迟,加上组件重渲染时字体类名的应用有短暂间隙,会导致页面短暂显示系统 fallback 字体,看起来像是字体权重被重置。


解决方案

1. 将字体定义移至单独文件

创建独立的字体管理文件,避免从根布局模块导出字体,确保字体对象引用稳定:

// app/fonts.ts
import { Inter, Russo_One } from "next/font/google";

export const inter = Inter({ subsets: ["latin"] });
export const russo = Russo_One({ weight: "400", subsets: ["latin"] });

修改根布局导入:

import "./globals.css";
import type { Metadata } from "next";
import { inter } from "./fonts";
import Navbar from "./_components/Navbar";

export const metadata: Metadata = {
  title: "Sportly",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Navbar />
        {children}
      </body>
    </html>
  );
}

修改 Navbar 导入:

import { Link, Typography, Box } from "@mui/material";
import { russo } from "../fonts";

export default function Navbar() {
  console.log("navbar rendering...");
  return (
    <nav className="/*STYLES*/">
        <h1 style={{fontWeight: "400" /* STYLES*/}} className={russo.className}>
          SPORTLY
        </h1>
    </nav>
  );
}

2. 用 React.memo 包裹 Navbar

如果字体引用稳定后仍存在重渲染,使用 React.memo 阻止组件不必要的重渲染:

import { Link, Typography, Box } from "@mui/material";
import { russo } from "../fonts";
import { memo } from "react";

const Navbar = () => {
  console.log("navbar rendering...");
  return (
    <nav className="/*STYLES*/">
        <h1 style={{fontWeight: "400" /* STYLES*/}} className={russo.className}>
          SPORTLY
        </h1>
    </nav>
  );
};

export default memo(Navbar);

3. 消除字体闪烁

在全局样式中为目标元素设置 fallback 字体的一致权重,减少视觉闪烁:

/* app/globals.css */
h1 {
  font-weight: 400;
  font-family: 'Russo One', sans-serif;
}

补充说明

开发模式下 Next.js 的热更新机制可能会导致一些额外渲染,生产模式下这类问题会大幅缓解,建议在生产环境测试验证最终效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 23:35:16