页面与布局
Next.js 13 中的应用路由 (App Router) 引入了新的文件约定,可轻松创建页面、共享布局和模板。本指南将介绍如何在 Next.js 应用中使用这些特殊文件。
页面
页面是路由对应的唯一用户界面。您可以通过从 page.js
文件导出组件来定义页面。使用嵌套文件夹定义路由,并通过 page.js
文件使路由可公开访问。
在 app
目录中添加 page.js
文件来创建您的第一个页面:

// `app/page.tsx` 是 `/` 路由对应的界面
export default function Page() {
return <h1>你好,首页!</h1>
}
// `app/page.js` 是 `/` 路由对应的界面
export default function Page() {
return <h1>你好,首页!</h1>
}
// `app/dashboard/page.tsx` 是 `/dashboard` 路由对应的界面
export default function Page() {
return <h1>你好,仪表盘页面!</h1>
}
// `app/dashboard/page.js` 是 `/dashboard` 路由对应的界面
export default function Page() {
return <h1>你好,仪表盘页面!</h1>
}
须知:
- 页面始终是路由子树的叶节点
- 可使用
.js
、.jsx
或.tsx
文件扩展名定义页面- 必须使用
page.js
文件才能使路由段可公开访问- 页面默认为服务端组件 (Server Components),但可设置为客户端组件 (Client Component)
- 页面可以获取数据。查看数据获取章节了解更多
布局
布局是多个页面间共享的用户界面。在导航时,布局会保持状态、维持交互且不会重新渲染。布局还可以嵌套。
您可以通过从 layout.js
文件默认导出一个 React 组件来定义布局。该组件应接收 children
属性,该属性在渲染时会被填充为子布局(如果存在)或子页面。

export default function DashboardLayout({
children, // 将是一个页面或嵌套布局
}: {
children: React.ReactNode
}) {
return (
<section>
{/* 在此处添加共享 UI 如页眉或侧边栏 */}
<nav></nav>
{children}
</section>
)
}
export default function DashboardLayout({
children, // 将是一个页面或嵌套布局
}) {
return (
<section>
{/* 在此处添加共享 UI 如页眉或侧边栏 */}
<nav></nav>
{children}
</section>
)
}
须知:
- 最顶层的布局称为根布局 (Root Layout)。这个必需的布局会被应用中的所有页面共享。根布局必须包含
html
和body
标签- 任何路由段都可以选择定义自己的布局。这些布局将在该段的所有页面间共享
- 路由中的布局默认会嵌套。每个父布局会通过 React 的
children
属性包裹其下方的子布局- 您可以使用路由组 (Route Groups)来选择性地包含或排除特定路由段的共享布局
- 布局默认为服务端组件 (Server Components),但可设置为客户端组件 (Client Component)
- 布局可以获取数据。查看数据获取章节了解更多
- 无法在父布局与其子级之间传递数据。但您可以在路由中多次获取相同数据,React 会自动去重请求且不影响性能
- 布局无法访问其下方的路由段。要在客户端组件中访问所有路由段,可使用
useSelectedLayoutSegment
或useSelectedLayoutSegments
- 可使用
.js
、.jsx
或.tsx
文件扩展名定义布局- 同一文件夹中可同时定义
layout.js
和page.js
文件。布局会包裹页面
根布局 (必需)
根布局定义在 app
目录的顶层,适用于所有路由。此布局允许您修改从服务器返回的初始 HTML。
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
须知:
app
目录必须包含根布局- 根布局必须定义
<html>
和<body>
标签,因为 Next.js 不会自动创建它们- 您可以使用内置 SEO 支持来管理
<head>
HTML 元素,例如<title>
元素- 您可以使用路由组 (Route Groups)创建多个根布局。查看示例
- 根布局默认为服务端组件 (Server Components),且不能设置为客户端组件 (Client Component)
从
pages
目录迁移: 根布局替代了_app.js
和_document.js
文件。查看迁移指南
嵌套布局
定义在文件夹内的布局(如 app/dashboard/layout.js
)适用于特定路由段(如 acme.com/dashboard
),并在这些段激活时渲染。默认情况下,文件层次结构中的布局会嵌套,即通过 children
属性包裹子布局。

export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}
export default function DashboardLayout({ children }) {
return <section>{children}</section>
}
须知:
- 只有根布局可以包含
<html>
和<body>
标签
如果结合上述两个布局,根布局 (app/layout.js
) 会包裹仪表盘布局 (app/dashboard/layout.js
),而仪表盘布局会包裹 app/dashboard/*
内的路由段。
这两个布局会按如下方式嵌套:

您可以使用路由组 (Route Groups)来选择性地包含或排除特定路由段的共享布局。
模板
模板与布局类似,会包裹每个子布局或页面。但与跨路由保持状态的布局不同,模板在导航时会为每个子级创建新实例。这意味着当用户在共享模板的路由间导航时,会挂载组件的新实例、重新创建 DOM 元素、不保留状态并重新同步副作用。
在某些需要这些特定行为的场景下,模板比布局更合适。例如:
- 依赖
useEffect
的功能(如记录页面访问)和useState
(如每页反馈表单) - 更改框架默认行为。例如,布局内的 Suspense 边界仅在首次加载布局时显示回退内容,而模板会在每次导航时显示回退内容
可以通过从 template.js
文件默认导出一个 React 组件来定义模板。该组件应接收 children
属性。

export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
export default function Template({ children }) {
return <div>{children}</div>
}
在嵌套方面,template.js
会在布局与其子级之间渲染。以下是简化输出:
<Layout>
{/* 注意模板具有唯一键 */}
<Template key={routeParam}>{children}</Template>
</Layout>
修改 <head>
在 app
目录中,您可以使用内置 SEO 支持修改 <head>
HTML 元素,如 title
和 meta
。
通过在 layout.js
或 page.js
文件中导出 metadata
对象或 generateMetadata
函数来定义元数据。
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js',
}
export default function Page() {
return '...'
}
export const metadata = {
title: 'Next.js',
}
export default function Page() {
return '...'
}
须知:您不应手动添加
<head>
标签(如<title>
和<meta>
)到根布局中。而应使用元数据 API (Metadata API),它能自动处理高级需求,如流式传输和去重<head>
元素。