Skip to main content

页面和布局

Next.js 13 中的 App Router 引入了新的文件约定,可以轻松创建页面、共享布局和模板。 本页面将指导您如何在 Next.js 应用程序中使用这些特殊文件。

页面

页面是特定路由的UI。 您可以通过从page.js文件中导出组件来定义页面。 使用嵌套文件夹来定义路由,并使用page.js文件使路由公开可访问。

通过在app目录中添加page.js文件来创建您的第一个页面: page special file

app/page.tsx
// `app/page.tsx` is the UI for the `/` URL
export default function Page() {
return <h1>Hello, Home page!</h1>
}
app/dashboard/page.tsx
// `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属性,在渲染期间将其填充为子布局(如果存在)或子页面。 layout special file

app/dashboard/layout.tsx
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>
)
}

值得知道:

  • 最上层的布局称为根布局。 这是一个必需的布局,它在应用程序中的所有页面之间共享。 根布局必须包含htmlbody标签。
  • 任何路由段都可以选择性地定义自己的布局。这些布局将在该段中的所有页面之间共享。
  • 路由中的布局默认是嵌套的。每个父布局使用React children属性包装其下方的子布局。
  • 您可以使用Route Groups选择性地将特定路由段放入和移出共享布局。
  • 布局默认是Server Components,但可以设置为Client Components。
  • 布局可以获取数据。有关更多信息,请查看数据获取部分。
  • 在父布局和其子布局之间传递数据是不可能的。 但是,您可以在路由中多次获取相同的数据,而React将自动去重请求,而不会影响性能。
  • 布局无法访问其下方的路由段。要访问所有路由段,可以在Client Component中使用useSelectedLayoutSegmentuseSelectedLayoutSegments
  • 可以使用.js.jsx.tsx文件扩展名用于布局。
  • 可以在同一文件夹中定义layout.jspage.js文件。布局将包裹页面

根布局(必需)

根布局在app目录的顶层定义,并应用于所有路由。此布局使您能够修改从服务器返回的初始HTML。

app/layout.tsx
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属性包装子布局。

nested layout

app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}

值得知道:

  • 只有根布局可以包含<html><body>标签。

如果将上述两个布局合并, 根布局(app/layout.js)将包装仪表板布局(app/dashboard/layout.js), 后者将包装app/dashboard/*内的路由段。

这两个布局将嵌套如下: nested layouts UI

可以使用Route Groups选择性地将特定路由段放入和移出共享布局。

模板

模板与布局相似,因为它们包装每个子布局或页面。 与布局不同的是,模板为导航中的每个子项创建一个新实例。 这意味着当用户在共享模板的路由之间导航时,该组件的新实例被挂载, DOM元素被重新创建,状态不保留,并且效果重新同步。

可能有些情况下,您需要这些特定的行为,而模板将比布局更合适。例如:

  • 依赖于useEffect(例如记录页面视图)和useState(例如每页反馈表单)的功能。
  • 更改默认框架行为。例如,布局内的Suspense边界仅在第一次加载布局时显示回退,而在切换页面时不会显示。对于模板,每次导航都会显示回退。

可以通过从template.js文件中导出一个默认的React组件来定义模板。组件应该接受一个children属性。 template special file

app/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>
}

就嵌套而言,template.js在布局和其子布局之间呈现。以下是一个简化的输出:

Output
<Layout>
{/* 注意,模板被赋予唯一的键。 */}
<Template key={routeParam}>{children}</Template>
</Layout>

修改<head>

app目录中,您可以使用内置的SEO支持来修改<head> HTML元素,例如标题和meta。

可以通过在layout.jspage.js文件中导出一个metadata对象或 generateMetadata函数来定义元数据。

app/page.tsx
import { Metadata } from 'next';

export const metadata: Metadata = {
title: 'Next.js',
};

export default function Page() {
return '...';
}
note

您不应手动添加<head>标签(如<title><meta>)到根布局。 相反,您应该使用Metadata API,该API会自动处理高级要求,如流式传输和去重<head>元素。