Skip to main content

布局和模版

特殊文件layout.jstemplate.js 让您创建可在 路由间共享的 UI。本页面将知道您如何及何时使用这些特殊文件。

布局

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

您能够通过在layout.js文件中默认输出一个 React 组件来定义一个布局。该组件应该接收一个children属性,用作在渲染过程中填充子布局或页面。

例如,以下布局将会被/dashboard/dashboard/settings 页面共享。

layout.js special file
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标签,允许您修改由服务器返回的初始化 HTML。

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

嵌套布局

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

例如,在dashboard文件夹中新建一个layout.js文件来创建/dashboard路由的布局。

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

如果您要合并上面的两个布局, 根布局 (app/layout.js) 将会包裹 dashboard 布局 (app/dashboard/layout.js), 从而生成 app/dashboard/*路由段.

这两个布局将这样嵌套:

Nested Layouts

您需要知道:

  • 布局能够使用.js, .jsx, or .tsx文件扩展名。
  • 只有根布局能够包含 <html><body> 标签。
  • 当在同一文件夹下定义一个layout.js文件和一个page.js文件时, layout 将会包裹 page。
  • 默认情况下,布局是 服务器端组件 ,但是能够设置为客户端组件.
  • 布局能够获取数据。查看数据获取 部分来了解更多。
  • 不能再父子布局间传递数据。不过,您可以在路由中多次获取相同的数据,并且 React 会 自动清除重复数据请求而不会影响性能。
  • 布局无法访问pathname (了解更多). 但是引入的客户端组件能够通过usePathname hook 访问 pathname。
  • 布局无法访问其下方的路由段。您可以在客户端组件中使用useSelectedLayoutSegmentuseSelectedLayoutSegments来访问所有路由段。
  • 您能够使用路由组来选择共享布局中的具体路由段。
  • 您你能够使用路由组来创建多个根布局.查看示例.

模版

模版在包裹子布局或页面方面与布局类似。不同于布局在跨路由中持续保持状态,模版在导航时为它们的每个子项创建了新的实力。这意味着用户在共享模版的路由间导航时,会挂载一个新的子实例,重新创建 DOM 元素,客户端组件不会保留状态并重新同步效果。

也许有些情况下,你需要这些特定行为,并且模版会是相对于布局更为适合的选择。例如:

  • 导航时重新同步 useEffect
  • 导航时重置子客户端组件的状态。

模版可能通过在一个template.js文件中导出一个默认 React 组件来定义。且组件应该接收一个children属性。

template.js special file
export default function Template({ children }: { children: React.ReactNode }) {
return <div>{children}</div>;
}
export default function Template({ children }) {
return <div>{children}</div>;
}

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

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

例子

Metadata

您能够使用Metadata APIs修改 HTML 元素<head>,如:titlemeta

Metadaya 能够由在 layout.jspage.js文件中输出metadata 对象generateMetadata function来定义。

import type { 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>。 相反, 使用自动处理高级需求的Metadata API,如:流式处理和重复数据删除<head>元素.

API reference了解更多关于可用的 metadata 选项。

Active 导航链接

您可以使用usePathname() hook 来决定是否导航链接处于活动状态。

因为 usePathname()是客户端 hook,您需要将导航链接提取进客户端组件,该组件可以导入进您的布局或模版中:

"use client";

import { usePathname } from "next/navigation";
import Link from "next/link";

export function NavLinks() {
const pathname = usePathname();

return (
<nav>
<Link className={`link ${pathname === "/" ? "active" : ""}`} href="/">
Home
</Link>

<Link
className={`link ${pathname === "/about" ? "active" : ""}`}
href="/about"
>
About
</Link>
</nav>
);
}
"use client";

import { usePathname } from "next/navigation";
import Link from "next/link";

export function Links() {
const pathname = usePathname();

return (
<nav>
<Link className={`link ${pathname === "/" ? "active" : ""}`} href="/">
Home
</Link>

<Link
className={`link ${pathname === "/about" ? "active" : ""}`}
href="/about"
>
About
</Link>
</nav>
);
}
import { NavLinks } from "@/app/ui/nav-links";

export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<NavLinks />
<main>{children}</main>
</body>
</html>
);
}
import { NavLinks } from "@/app/ui/nav-links";

export default function Layout({ children }) {
return (
<html lang="en">
<body>
<NavLinks />
<main>{children}</main>
</body>
</html>
);
}