Pages and Layouts 页面和布局

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.

特殊文件 layout.js、page.js 和 template.js 允许您为路由创建 UI。此页面将指导您如何以及何时使用这些特殊文件。

1.Pages

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.

页面是特定于某个路由的 UI。您可以通过默认从 page.js 文件导出组件来定义页面。

For example, to create your index page, add the page.js file inside the app directory:

例如,要创建 index 页面,请在 app 目录中添加 page.js 文件:

ps: 演示均为app顶级目录

// `app/page.tsx` is the UI for the `/` URL
export default function Page() {
  return <h1>Hello, Home page!</h1>
}

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:

然后,要创建更多页面,创建一个新文件夹并在其中添加 page.js 文件。例如,要为 /dashboard 路由创建一个页面,创建一个名为 dashboard 的新文件夹,并在其中添加 page.js 文件:

// `app/dashboard/page.tsx` is the UI for the `/dashboard` URL
export default function Page() {
  return <h1>Hello, Dashboard Page!</h1>
}

Good to know: 须知:

  • The .js, .jsx, or .tsx file extensions can be used for Pages.
    页面可以使用 .js.jsx.tsx 文件扩展名。

  • A page is always the leaf of the route subtree.
    页面始终是路由子树的叶节点。

  • A page.js file is required to make a route segment publicly accessible.
    要使路由段可公开访问,需要一个 page.js 文件。

  • 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.
    页面可以获取数据。有关更多信息,请参阅数据获取部分。 页面

ps: 时刻注意在app的顶级目录中,其所属默认为服务器组件,但是部分项目在此处混淆了。

2.Layouts 布局

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.

布局是多个路由之间共享的 UI。在导航时,布局会保留状态、保持交互性且不会重新渲染。布局还可以嵌套。

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.

您可以通过默认从 layout.js 文件导出 React 组件来定义布局。该组件应接受一个 children prop,该 prop 将在渲染期间填充子布局(如果存在)或页面。

For example, the layout will be shared with the /dashboard and /dashboard/settings pages:

例如,布局将与 /dashboard 和 /dashboard/settings 页面共享:

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>
  )
}

a.Root Layout (Required) 根布局

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.

根布局在 app 目录的顶层定义,并适用于所有路由。此布局是必需的,并且必须包含 html 和 body 标记,以便您可以修改从服务器返回的初始 HTML。

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {/* Layout UI */}
        <main>{children}</main>
      </body>
    </html>
  )
}

b.Nesting Layouts 嵌套布局

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).

默认情况下,文件夹层次结构中的布局是嵌套的,这意味着它们通过其 children 属性包装子布局。您可以在特定路由段(文件夹)中添加 layout.js 来嵌套布局。

For example, to create a layout for the /dashboard route, add a new layout.js file inside the dashboard folder:

例如,要为 /dashboard 路由创建布局,请在 dashboard 文件夹中添加一个新的 layout.js 文件:

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

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/*.

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

The two layouts would be nested as such:

两个布局将按如下方式嵌套:

Good to know: 值得了解:

  • .js, .jsx, or .tsx file extensions can be used for Layouts.
    .js.jsx.tsx 文件扩展名可用于布局。

  • Only the root layout can contain <html> and <body> tags.
    只有根布局可以包含 <html><body> 标签。

  • When a layout.js and page.js file are defined in the same folder. The layout will wrap the page.
    layout.jspage.js 文件定义在同一个文件夹中时。布局将包装页面。

  • 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.
    布局可以获取数据。有关更多信息,请参阅数据获取部分。

  • 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.
    父布局与其子布局之间无法传递数据。但是,您可以在路由中多次获取相同的数据,React 会自动对请求进行去重,而不会影响性能。

  • Layouts do not have access to the route segments below itself. To access all route segments, you can use useSelectedLayoutSegment or useSelectedLayoutSegments in a Client Component.
    布局无法访问其下方的路由段。要访问所有路由段,您可以在客户端组件中使用 useSelectedLayoutSegmentuseSelectedLayoutSegments

  • 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.
    您可以使用路由组创建多个根布局。在此处查看示例。

  • Migrating from the pages directory: The root layout replaces the _app.js and _document.js files. View the migration guide.
    pages 目录迁移:根布局替换 _app.js_document.js 文件。查看迁移指南。

3.Templates 模板

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.

模板与布局类似,它们都包装每个子布局或页面。与跨路由并保持状态的布局不同,模板为其每个子项创建一个新实例以进行导航。这意味着当用户在共享模板的路由之间导航时,将装载组件的新实例,重新创建 DOM 元素,不保留状态,并且重新同步效果。

There may be cases where you need those specific behaviors, and templates would be a more suitable option than layouts. For example:

可能存在需要那些特定行为的情况,模板将比布局更适合作为选项。例如:

-Features that rely on useEffect (e.g logging page views) and useState (e.g a per-page feedback form).

依赖于 useEffect (例如记录页面浏览量)和 useState (例如每个页面的反馈表单)的功能。

-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.

要更改默认框架行为。例如,布局内的 Suspense Boundaries 仅在首次加载布局时显示后备,而不是在切换页面时显示。对于模板,后备在每次导航时显示。

A template can be defined by exporting a default React component from a template.js file. The component should accept a children prop.

可以通过从 template.js 文件导出默认 React 组件来定义模板。该组件应接受 children 道具。

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

In terms of nesting, template.js is rendered between a layout and its children. Here's a simplified output:

在嵌套方面, template.js 呈现在布局及其子项之间。这是一个简化的输出:

<Layout>
  {/* Note that the template is given a unique key. */}
  <Template key={routeParam}>{children}</Template>
</Layout>

4.Metadata 元数据

In the app directory, you can modify the <head> HTML elements such as title and meta using the Metadata APIs.

在 app 目录中,您可以使用元数据 API 修改 <head> HTML 元素,例如 title 和 meta 。

Metadata can be defined by exporting a metadata object or generateMetadata function in a layout.js or page.js file.

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

import { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Next.js',
}
 
export default function Page() {
  return '...'
}

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.

温馨提示:您不应手动将 <head> 标签(例如 <title> 和 <meta> )添加到根布局。相反,您应使用元数据 API,它会自动处理高级要求,例如流式传输和对 <head> 元素进行重复数据删除。