页面和布局
Next.js 13 中的 App Router 引入了新的文件约定,可以轻松创建页面、共享布局和模板。 本页面将指导您如何在 Next.js 应用程序中使用这些特殊文件。
页面
页面是特定路由的UI。
您可以通过从page.js
文件中导出组件来定义页面。
使用嵌套文件夹来定义路由,并使用page.js
文件使路由公开可访问。
通过在app
目录中添加page.js
文件来创建您的第一个页面:
// `app/page.tsx` is the UI for the `/` URL
export default function Page() {
return <h1>Hello, Home page!</h1>
}
// `app/dashboard/page.tsx` is the UI for the `/dashboard` URL
export default function Page() {
return <h1>Hello, Dashboard Page!</h1>
}
值得知道:
- 页面始终是路由子树的叶子。
- 可以使用
.js
、.jsx
或.tsx
文件扩展名用于页面。 - 必须使用
page.js
文件才能使路由段公开可访问。 - 页面默认是Server Components,但可以设置为Client Components。
- 页面可以获取数据。有关更多信息,请查看数据获取部分。
布局
布局是在多个页面之间共享的UI。 在导航时,布局保留状态,保持交互,并且不重新呈现。 布局也可以是嵌套的。
您可以通过从layout.js
文件中默认导出一个React组件来定义布局。
组件应接受一个children
属性,在渲染期间将其填充为子布局(如果存在)或子页面。
export default function DashboardLayout({
children, // will be a page or nested layout
}: {
children: React.ReactNode
}) {
return (
<section>
{/* Include shared UI here e.g. a header or sidebar */}
<nav></nav>
{children}
</section>
)
}
值得知道:
- 最上层的布局称为根布局。
这是一个必需的布局,它在应用程序中的所有页面之间共享。
根布局必须包含
html
和body
标签。 - 任何路由段都可以选择性地定义自己的布局。这些布局将在该段中的所有页面之间共享。
- 路由中的布局默认是嵌套的。每个父布局使用React
children
属性包装其下方的子布局。 - 您可以使用Route Groups选择性地将特定路由段放入和移出共享布局。
- 布局默认是Server Components,但可以设置为Client Components。
- 布局可以获取数据。有关更多信息,请查看数据获取部分。
- 在父布局和其子布局之间传递数据是不可能的。 但是,您可以在路由中多次获取相同的数据,而React将自动去重请求,而不会影响性能。
- 布局无法访问其下方的路由段。要访问所有路由段,可以在Client Component中使用
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>
)
}
值得知道:
app
目录必须包含一个根布局。- 根布局必须定义
<html>
和<body>
标签,因为Next.js不会自动创建它们。 - 您可以使用内置的SEO支持来管理
<head>
HTML元素,例如<title>
元素。 - 您可以使用路由组创建多个根布局。查看此处的示例。
- 根布局默认是Server Components,不能设置为Client Components。
- 从
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>
}
值得知道:
- 只有根布局可以包含
<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>
}
就嵌套而言,template.js
在布局和其子布局之间呈现。以下是一个简化的输出:
<Layout>
{/* 注意,模板被赋予唯一的键。 */}
<Template key={routeParam}>{children}</Template>
</Layout>
修改<head>
在app
目录中,您可以使用内置的SEO支持来修改<head>
HTML元素,例如标题和meta。
可以通过在layout.js
或page.js
文件中导出一个metadata
对象或
generateMetadata
函数来定义元数据。
import { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Next.js',
};
export default function Page() {
return '...';
}
您不应手动添加<head>
标签(如<title>
和<meta>
)到根布局。
相反,您应该使用Metadata API,该API会自动处理高级要求,如流式传输和去重<head>
元素。