页面和布局
我们建议你先阅读 路由基础知识 和 定义路由 页,然后再继续。
¥We recommend reading the Routing Fundamentals and Defining Routes pages before continuing.
特殊文件 layout.js、page.js 和 template.js 允许你为 route 创建 UI。本页将指导你了解如何以及何时使用这些特殊文件。
¥The special files layout.js, page.js, and template.js allow you to create UI for a route. This page will guide you through how and when to use these special files.
页面
¥Pages
页面是路由特有的 UI。你可以通过默认从 page.js
文件导出组件来定义页面。
¥A page is UI that is unique to a route. You can define a page by default exporting a component from a page.js
file.
例如,要创建 index
页面,请在 app
目录中添加 page.js
文件:
¥For example, to create your index
page, add the page.js
file inside the app
directory:
// `app/page.tsx` is the UI for the `/` URL
export default function Page() {
return <h1>Hello, Home page!</h1>
}
// `app/page.js` is the UI for the `/` URL
export default function Page() {
return <h1>Hello, Home page!</h1>
}
然后,要创建更多页面,请创建一个新文件夹并在其中添加 page.js
文件。例如,要为 /dashboard
路由创建页面,请创建一个名为 dashboard
的新文件夹,并在其中添加 page.js
文件:
¥Then, to create further pages, create a new folder and add the page.js
file inside it. For example, to create a page for the /dashboard
route, create a new folder called dashboard
, and add the page.js
file inside it:
// `app/dashboard/page.tsx` is the UI for the `/dashboard` URL
export default function Page() {
return <h1>Hello, Dashboard Page!</h1>
}
// `app/dashboard/page.js` is the UI for the `/dashboard` URL
export default function Page() {
return <h1>Hello, Dashboard Page!</h1>
}
很高兴知道:
¥Good to know:
.js
、.jsx
或.tsx
文件扩展名可用于页面。¥The
.js
,.jsx
, or.tsx
file extensions can be used for Pages.¥A page is always the leaf of the route subtree.
需要
page.js
文件才能公开访问路段。¥A
page.js
file is required to make a route segment publicly accessible.¥Pages are Server Components by default, but can be set to a Client Component.
页面可以获取数据。查看 数据获取 部分了解更多信息。
¥Pages can fetch data. View the Data Fetching section for more information.
布局
¥Layouts
布局是在多个路由之间共享的 UI。在导航时,布局保留状态、保持交互性并且不重新渲染。布局也可以是 nested。
¥A layout is UI that is shared between multiple routes. On navigation, layouts preserve state, remain interactive, and do not re-render. Layouts can also be nested.
你可以通过默认从 layout.js
文件导出 React 组件来定义布局。该组件应该接受 children
属性,该属性将在渲染期间填充子布局(如果存在)或页面。
¥You can define a layout by default exporting a React component from a layout.js
file. The component should accept a children
prop that will be populated with a child layout (if it exists) or a page during rendering.
例如,布局将与 /dashboard
和 /dashboard/settings
页面共享:
¥For example, the layout will be shared with the /dashboard
and /dashboard/settings
pages:
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>
)
}
export default function DashboardLayout({
children, // will be a page or nested layout
}) {
return (
<section>
{/* Include shared UI here e.g. a header or sidebar */}
<nav></nav>
{children}
</section>
)
}
根布局(必需)
¥Root Layout (Required)
根布局在 app
目录的顶层定义并适用于所有路由。此布局是必需的,并且必须包含 html
和 body
标记,允许你修改从服务器返回的初始 HTML。
¥The root layout is defined at the top level of the app
directory and applies to all routes. This layout is required and must contain html
and body
tags, allowing you to modify the initial HTML returned from the server.
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
{/* Layout UI */}
<main>{children}</main>
</body>
</html>
)
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{/* Layout UI */}
<main>{children}</main>
</body>
</html>
)
}
嵌套布局
¥Nesting Layouts
默认情况下,文件夹层次结构中的布局是嵌套的,这意味着它们通过 children
属性封装子布局。你可以通过在特定路由段(文件夹)内添加 layout.js
来嵌套布局。
¥By default, layouts in the folder hierarchy are nested, which means they wrap child layouts via their children
prop. You can nest layouts by adding layout.js
inside specific route segments (folders).
例如,要为 /dashboard
路由创建布局,请在 dashboard
文件夹中添加新的 layout.js
文件:
¥For example, to create a layout for the /dashboard
route, add a new layout.js
file inside the dashboard
folder:
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}
export default function DashboardLayout({ children }) {
return <section>{children}</section>
}
如果要组合上面的两个布局,根布局 (app/layout.js
) 将封装仪表板布局 (app/dashboard/layout.js
),仪表板布局 (app/dashboard/layout.js
) 将封装路由段在 app/dashboard/*
内。
¥If you were to combine the two layouts above, the root layout (app/layout.js
) would wrap the dashboard layout (app/dashboard/layout.js
), which would wrap route segments inside app/dashboard/*
.
这两个布局将这样嵌套:
¥The two layouts would be nested as such:
很高兴知道:
¥Good to know:
.js
、.jsx
或.tsx
文件扩展名可用于布局。¥
.js
,.jsx
, or.tsx
file extensions can be used for Layouts.只有根布局可以包含
<html>
和<body>
标签。¥Only the root layout can contain
<html>
and<body>
tags.当
layout.js
和page.js
文件定义在同一文件夹中时,布局将换行页面。¥When a
layout.js
andpage.js
file are defined in the same folder, the layout will wrap the page.¥Layouts are Server Components by default but can be set to a Client Component.
布局可以获取数据。查看 数据获取 部分了解更多信息。
¥Layouts can fetch data. View the Data Fetching section for more information.
在父布局与其子布局之间传递数据是不可能的。然而,你可以在一个路由中多次获取相同的数据,React 将 自动删除请求的重复数据 而不影响性能。
¥Passing data between a parent layout and its children is not possible. However, you can fetch the same data in a route more than once, and React will automatically dedupe the requests without affecting performance.
布局无法访问其自身下方的路由段。要访问所有路由段,你可以在客户端组件中使用
useSelectedLayoutSegment
或useSelectedLayoutSegments
。¥Layouts do not have access to the route segments below itself. To access all route segments, you can use
useSelectedLayoutSegment
oruseSelectedLayoutSegments
in a Client Component.你可以使用 路由组 在共享布局中选择特定路由段。
¥You can use Route Groups to opt specific route segments in and out of shared layouts.
¥You can use Route Groups to create multiple root layouts. See an example here.
从
pages
目录迁移:根布局替换_app.js
和_document.js
文件。查看迁移指南。¥Migrating from the
pages
directory: The root layout replaces the_app.js
and_document.js
files. View the migration guide.
模板
¥Templates
模板与布局类似,它们封装每个子布局或页面。与跨路由持续存在并维护状态的布局不同,模板为导航上的每个子级创建一个新实例。这意味着当用户在共享模板的路由之间导航时,将安装组件的新实例,重新创建 DOM 元素,不保留状态,并且重新同步效果。
¥Templates are similar to layouts in that they wrap each child layout or page. Unlike layouts that persist across routes and maintain state, templates create a new instance for each of their children on navigation. This means that when a user navigates between routes that share a template, a new instance of the component is mounted, DOM elements are recreated, state is not preserved, and effects are re-synchronized.
在某些情况下,你可能需要这些特定行为,而模板将是比布局更合适的选择。例如:
¥There may be cases where you need those specific behaviors, and templates would be a more suitable option than layouts. For example:
-
依赖于
useEffect
(例如记录页面浏览量)和useState
(例如每页反馈表)的功能。¥Features that rely on
useEffect
(e.g logging page views) anduseState
(e.g a per-page feedback form). -
更改默认框架行为。例如,布局内的 Suspense Boundaries 仅在第一次加载布局时显示回退,而不是在切换页面时显示回退。对于模板,后备显示在每个导航上。
¥To change the default framework behavior. For example, Suspense Boundaries inside layouts only show the fallback the first time the Layout is loaded and not when switching pages. For templates, the fallback is shown on each navigation.
可以通过从 template.js
文件导出默认的 React 组件来定义模板。该组件应该接受 children
属性。
¥A template can be defined by exporting a default React component from a template.js
file. The component should accept a children
prop.
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}
export default function Template({ children }) {
return <div>{children}</div>
}
在嵌套方面,template.js
在布局及其子布局之间渲染。这是一个简化的输出:
¥In terms of nesting, template.js
is rendered between a layout and its children. Here's a simplified output:
<Layout>
{/* Note that the template is given a unique key. */}
<Template key={routeParam}>{children}</Template>
</Layout>
元数据
¥Metadata
在 app
目录中,你可以使用 元数据 API 修改 <head>
HTML 元素,例如 title
和 meta
。
¥In the app
directory, you can modify the <head>
HTML elements such as title
and meta
using the Metadata APIs.
可以通过导出 layout.js
或 page.js
文件中的 metadata
对象 或 generateMetadata
功能 来定义元数据。
¥Metadata can be defined by exporting a metadata
object or generateMetadata
function in a layout.js
or page.js
file.
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,它会自动处理高级要求,例如流式传输和删除<head>
元素的重复数据。¥Good to know: You should not manually add
<head>
tags such as<title>
and<meta>
to root layouts. Instead, you should use the Metadata API which automatically handles advanced requirements such as streaming and de-duplicating<head>
elements.
了解有关 API 参考 中可用元数据选项的更多信息
¥Learn more about available metadata options in the API reference