layout.js
layout
文件用于定义 Next.js 应用中的布局。
¥The layout
file is used to define a layout in your Next.js application.
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}
根布局是根 app
目录中的最顶层布局。它用于定义 <html>
和 <body>
标签以及其他全局共享的 UI。
¥A root layout is the top-most layout in the root app
directory. It is used to define the <html>
and <body>
tags and other globally shared UI.
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
参考
¥Reference
属性
¥Props
children
(必需的)
¥children
(required)
布局组件应该接受并使用 children
属性。在渲染期间,children
将填充布局正在环绕的路由段。这些主要是子 布局(如果存在)或 页面 的组件,但也可能是其他特殊文件,如适用时的 加载中 或 错误。
¥Layout components should accept and use a children
prop. During rendering, children
will be populated with the route segments the layout is wrapping. These will primarily be the component of a child Layout (if it exists) or Page, but could also be other special files like Loading or Error when applicable.
params
(可选)
¥params
(optional)
从根段到该布局解析为包含 动态路由参数 对象的对象的 promise。
¥A promise that resolves to an object containing the dynamic route parameters object from the root segment down to that layout.
export default async function Layout({
params,
}: {
params: Promise<{ team: string }>
}) {
const { team } = await params
}
示例路由 | URL | params |
---|---|---|
app/dashboard/[team]/layout.js | /dashboard/1 | Promise<{ team: '1' }> |
app/shop/[tag]/[item]/layout.js | /shop/1/2 | Promise<{ tag: '1', item: '2' }> |
app/blog/[...slug]/layout.js | /blog/1/2 | Promise<{ slug: ['1', '2'] }> |
由于
params
prop 是一个 promise。你必须使用async/await
或 React 的use
函数来访问值。¥Since the
params
prop is a promise. You must useasync/await
or React'suse
function to access the values.在版本 14 及更早版本中,
params
是一个同步 prop。为了帮助向后兼容,你仍然可以在 Next.js 15 中同步访问它,但此行为将来会被弃用。¥In version 14 and earlier,
params
was a synchronous prop. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
根布局
¥Root Layout
app
目录必须包含根 app/layout.js
。
¥The app
directory must include a root app/layout.js
.
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>{children}</body>
</html>
)
}
根布局必须定义
<html>
和<body>
标签。¥The root layout must define
<html>
and<body>
tags.你不应手动将
<head>
标签(例如<title>
和<meta>
)添加到根布局。相反,你应该使用 元数据 API,它会自动处理高级要求,例如流式传输和删除<head>
元素的重复数据。¥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.
你可以使用 路由组 创建多个根布局。
¥You can use route groups to create multiple root layouts.
跨多个根布局导航将导致完整页面加载(与客户端导航相反)。例如,从使用
app/(shop)/layout.js
的/cart
导航到使用app/(marketing)/layout.js
的/blog
将导致整页加载。这仅适用于多个根布局。¥Navigating across multiple root layouts will cause a full page load (as opposed to a client-side navigation). For example, navigating from
/cart
that usesapp/(shop)/layout.js
to/blog
that usesapp/(marketing)/layout.js
will cause a full page load. This only applies to multiple root layouts.
注意事项
¥Caveats
请求对象
¥Request Object
布局在导航期间会缓存在客户端,以避免不必要的服务器请求。
¥Layouts are cached in the client during navigation to avoid unnecessary server requests.
布局 不会重新渲染。它们可以被缓存和重用,以避免在页面之间导航时不必要的计算。通过限制布局访问原始请求,Next.js 可以防止布局内执行可能缓慢或昂贵的用户代码,这可能会对性能产生负面影响。
¥Layouts do not rerender. They can be cached and reused to avoid unnecessary computation when navigating between pages. By restricting layouts from accessing the raw request, Next.js can prevent the execution of potentially slow or expensive user code within the layout, which could negatively impact performance.
要访问请求对象,你可以使用 服务器组件 和函数中的 headers
和 cookies
API。
¥To access the request object, you can use headers
and cookies
APIs in Server Components and Functions.
import { cookies } from 'next/headers'
export default async function Layout({ children }) {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')
return '...'
}
查询参数
¥Query params
布局在导航时不会重新渲染,因此它们无法访问搜索参数,否则这些参数会变得过时。
¥Layouts do not rerender on navigation, so they cannot access search params which would otherwise become stale.
要访问更新的查询参数,你可以使用 Page searchParams
属性,或使用 useSearchParams
钩子在客户端组件中读取它们。由于客户端组件在导航时重新渲染,因此它们可以访问最新的查询参数。
¥To access updated query parameters, you can use the Page searchParams
prop, or read them inside a Client Component using the useSearchParams
hook. Since Client Components re-render on navigation, they have access to the latest query parameters.
'use client'
import { useSearchParams } from 'next/navigation'
export default function Search() {
const searchParams = useSearchParams()
const search = searchParams.get('search')
return '...'
}
import Search from '@/app/ui/search'
export default function Layout({ children }) {
return (
<>
<Search />
{children}
</>
)
}
路径名
¥Pathname
布局在导航时不会重新渲染,因此它们无法访问路径名,否则这些路径名会变得过时。
¥Layouts do not re-render on navigation, so they do not access pathname which would otherwise become stale.
要访问当前路径名,你可以使用 usePathname
钩子在客户端组件中读取它。由于客户端组件在导航期间重新渲染,因此它们可以访问最新的路径名。
¥To access the current pathname, you can read it inside a Client Component using the usePathname
hook. Since Client Components re-render during navigation, they have access to the latest pathname.
'use client'
import { usePathname } from 'next/navigation'
// Simplified breadcrumbs logic
export default function Breadcrumbs() {
const pathname = usePathname()
const segments = pathname.split('/')
return (
<nav>
{segments.map((segment, index) => (
<span key={index}>
{' > '}
{segment}
</span>
))}
</nav>
)
}
import { Breadcrumbs } from '@/app/ui/Breadcrumbs'
export default function Layout({ children }) {
return (
<>
<Breadcrumbs />
<main>{children}</main>
</>
)
}
获取数据
¥Fetching Data
布局无法将数据传递给其 children
。但是,你可以在路由中多次获取相同的数据,并使用 React cache
来重复请求,而不会影响性能。
¥Layouts cannot pass data to their children
. However, you can fetch the same data in a route more than once, and use React cache
to dedupe the requests without affecting performance.
或者,在 Next.js 中使用 fetch
时,请求会自动进行数据去重。
¥Alternatively, when using fetch
in Next.js, requests are automatically deduped.
export async function getUser(id: string) {
const res = await fetch(`https://.../users/${id}`)
return res.json()
}
import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'
export default async function Layout({ children }) {
const user = await getUser('1')
return (
<>
<nav>
{/* ... */}
<UserName user={user.name} />
</nav>
{children}
</>
)
}
import { getUser } from '@/app/lib/data'
import { UserName } from '@/app/ui/user-name'
export default async function Page() {
const user = await getUser('1')
return (
<div>
<h1>Welcome {user.name}</h1>
</div>
)
}
访问子段
¥Accessing child segments
布局无法访问其自身下方的路由段。要访问所有路由段,你可以在客户端组件中使用 useSelectedLayoutSegment
或 useSelectedLayoutSegments
。
¥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.
'use client'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function NavLink({
slug,
children,
}: {
slug: string
children: React.ReactNode
}) {
const segment = useSelectedLayoutSegment()
const isActive = slug === segment
return (
<Link
href={`/blog/${slug}`}
// Change style depending on whether the link is active
style={{ fontWeight: isActive ? 'bold' : 'normal' }}
>
{children}
</Link>
)
}
import { NavLink } from './nav-link'
import getPosts from './get-posts'
export default async function Layout({
children,
}: {
children: React.ReactNode
}) {
const featuredPosts = await getPosts()
return (
<div>
{featuredPosts.map((post) => (
<div key={post.id}>
<NavLink slug={post.slug}>{post.title}</NavLink>
</div>
))}
<div>{children}</div>
</div>
)
}
示例
¥Examples
元数据
¥Metadata
你可以使用 metadata
对象 或 generateMetadata
功能 修改 <head>
HTML 元素(例如 title
和 meta
)。
¥You can modify the <head>
HTML elements such as title
and meta
using the metadata
object or generateMetadata
function.
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js',
}
export default function Layout({ children }: { children: React.ReactNode }) {
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, use the Metadata APIs which automatically handles advanced requirements such as streaming and de-duplicating<head>
elements.
活动导航链接
¥Active Nav Links
你可以使用 usePathname
钩子来确定导航链接是否处于活动状态。
¥You can use the usePathname
hook to determine if a nav link is active.
由于 usePathname
是一个客户端钩子,你需要将导航链接提取到客户端组件中,然后将其导入到你的布局中:
¥Since usePathname
is a client hook, you need to extract the nav links into a Client Component, which can be imported into your layout:
'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>
)
}
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>
)
}
基于 params
显示内容
¥Displaying content based on params
使用 动态路由段,你可以根据 params
属性显示或获取特定内容。
¥Using dynamic route segments, you can display or fetch specific content based on the params
prop.
export default async function DashboardLayout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ team: string }>
}) {
const { team } = await params
return (
<section>
<header>
<h1>Welcome to {team}'s Dashboard</h1>
</header>
<main>{children}</main>
</section>
)
}
在客户端组件中读取 params
¥Reading params
in Client Components
要在客户端组件(不能是 async
)中使用 params
,你可以使用 React 的 use
函数来读取 promise:
¥To use params
in a Client Component (which cannot be async
), you can use React's use
function to read the promise:
'use client'
import { use } from 'react'
export default function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = use(params)
}
版本历史
¥Version History
版本 | 更改 |
---|---|
v15.0.0-RC | params 现在是一个 promise。codemod 可用。 |
v13.0.0 | layout 已引入。 |