布局与页面
Next.js 使用基于文件系统的路由,这意味着你可以使用文件夹和文件来定义路由。本页将指导你如何创建布局和页面,以及如何在其间进行链接。
创建页面
页面是在特定路由上渲染的 UI。要创建一个页面,在 app 目录下添加一个 page 文件并默认导出 React 组件。例如,要创建一个索引页面 (/)

export default function Page() {
return <h1>Hello Next.js!</h1>
}创建布局
布局是在多个页面之间共享的 UI。在导航时,布局会保留状态、保持交互性并且不会重新渲染。
你可以通过从 layout 文件默认导出 React 组件来定义布局。该组件应接受一个 children 属性,它可以是一个页面或另一个布局。
例如,要创建一个接受索引页面作为子组件的布局,请在 app 目录下添加一个 layout 文件

export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
{/* Layout UI */}
{/* Place children where you want to render a page or nested layout */}
<main>{children}</main>
</body>
</html>
)
}上面的布局称为根布局,因为它是在 app 目录的根部定义的。根布局是必需的,并且必须包含 html 和 body 标签。
创建嵌套路由
嵌套路由是由多个 URL 片段组成的路由。例如,/blog/[slug] 路由由三个片段组成
/(根片段)blog(片段)[slug](叶片段)
在 Next.js 中
- 文件夹用于定义映射到 URL 片段的路由片段。
- 文件(如
page和layout)用于创建在片段中显示的 UI。
要创建嵌套路由,你可以将文件夹相互嵌套。例如,要添加 /blog 路由,请在 app 目录中创建一个名为 blog 的文件夹。然后,为了使 /blog 可以公开访问,添加一个 page.tsx 文件

// Dummy imports
import { getPosts } from '@/lib/posts'
import { Post } from '@/ui/post'
export default async function Page() {
const posts = await getPosts()
return (
<ul>
{posts.map((post) => (
<Post key={post.id} post={post} />
))}
</ul>
)
}你可以继续嵌套文件夹以创建嵌套路由。例如,要为特定博客文章创建路由,请在 blog 内部创建一个新的 [slug] 文件夹并添加一个 page 文件

function generateStaticParams() {}
export default function Page() {
return <h1>Hello, Blog Post Page!</h1>
}将文件夹名称用方括号括起来(例如 [slug])会创建一个动态路由片段,用于根据数据生成多个页面。例如,博客文章、产品页面等。
嵌套布局
默认情况下,文件夹层次结构中的布局也是嵌套的,这意味着它们通过其 children 属性包装子布局。你可以通过在特定路由片段(文件夹)内添加 layout 来嵌套布局。
例如,要为 /blog 路由创建一个布局,请在 blog 文件夹中添加一个新的 layout 文件。

export default function BlogLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>{children}</section>
}如果你将上述两个布局组合起来,根布局 (app/layout.js) 将会包裹博客布局 (app/blog/layout.js),而博客布局又会包裹博客 (app/blog/page.js) 和博客文章页面 (app/blog/[slug]/page.js)。
创建动态片段
动态片段允许您创建从数据生成的路由。例如,您可以创建一个动态片段,根据博客文章数据生成路由,而不是手动为每篇博客文章创建路由。
要创建动态片段,请将片段(文件夹)名称用方括号括起来:[segmentName]。例如,在 app/blog/[slug]/page.tsx 路由中,[slug] 就是动态片段。
export default async function BlogPostPage({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await getPost(slug)
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
)
}嵌套在动态片段中的布局也可以访问 params 属性。
使用搜索参数渲染
在服务器组件页面中,您可以使用searchParams属性访问搜索参数
export default async function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
const filters = (await searchParams).filters
}使用 searchParams 会使您的页面进入动态渲染,因为它需要传入请求来读取搜索参数。
客户端组件可以使用 useSearchParams 钩子来读取搜索参数。
了解更多关于 useSearchParams 在静态渲染和动态渲染路由中的使用。
何时使用以及如何使用
- 当你需要搜索参数来加载页面数据(例如,分页、从数据库过滤)时,使用
searchParams属性。 - 当搜索参数仅在客户端使用(例如,过滤已通过属性加载的列表)时,使用
useSearchParams。 - 作为一项小优化,你可以在回调函数或事件处理器中使用
new URLSearchParams(window.location.search)来读取搜索参数,而不会触发重新渲染。
页面间链接
你可以使用 <Link> 组件在路由之间进行导航。<Link> 是一个内置的 Next.js 组件,它扩展了 HTML <a> 标签,提供了预取和客户端导航。
例如,要生成博客文章列表,从 next/link 导入 <Link> 并向组件传递一个 href 属性
import Link from 'next/link'
export default async function Post({ post }) {
const posts = await getPosts()
return (
<ul>
{posts.map((post) => (
<li key={post.slug}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
)
}须知:
<Link>是 Next.js 中在路由之间导航的主要方式。你也可以使用useRouter钩子进行更高级的导航。
路由属性辅助函数
Next.js 提供了实用工具类型,可从你的路由结构中推断出 params 和命名槽
- PageProps:
page组件的属性,包括params和searchParams。 - LayoutProps:
layout组件的属性,包括children和任何命名槽(例如@analytics等文件夹)。
这些是全局可用的辅助函数,在运行 next dev、next build 或 next typegen 时生成。
export default async function Page(props: PageProps<'/blog/[slug]'>) {
const { slug } = await props.params
return <h1>Blog post: {slug}</h1>
}export default function Layout(props: LayoutProps<'/dashboard'>) {
return (
<section>
{props.children}
{/* If you have app/dashboard/@analytics, it appears as a typed slot: */}
{/* {props.analytics} */}
</section>
)
}须知
- 静态路由会将
params解析为{}。PageProps和LayoutProps是全局辅助工具——无需导入。- 类型在
next dev、next build或next typegen期间生成。
这有帮助吗?




