Next.js 使用文件系统路由,这意味着您可以使用文件夹和文件来定义路由。本页面将指导您如何创建布局和页面,以及如何在它们之间进行链接。
页面是呈现在特定路由上的用户界面。要创建一个页面,在 app 目录中添加一个 page 文件 并默认导出一个 React 组件。例如,要创建索引页面 (/):

export default function Page() {
return <h1>Hello Next.js!</h1>;
}export default function Page() {
return <h1>Hello Next.js!</h1>;
}布局是在多个页面之间共享的用户界面。导航时,布局会保留状态,保持交互性,并且不会重新渲染。
您可以通过从一个 layout 文件 默认导出一个 React 组件来定义一个布局。该组件应接受一个 children prop,它可以是一个页面或另一个 布局。
例如,要创建一个接受您的索引页面作为子级的布局,在 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>
);
}export default function DashboardLayout({ children }) {
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 中:
page 和 layout)用于创建显示给某个段的用户界面。要创建嵌套路由,您可以将文件夹相互嵌套。例如,要为 /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>
);
}// 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>;
}function generateStaticParams() {}
export default function Page() {
return <h1>Hello, Blog Post Page!</h1>;
}将文件夹名称用方括号括起来(例如 [slug])会创建一个 动态路由段,用于从数据生成多个页面。例如,博客文章、产品页面等。
默认情况下,文件夹层次结构中的布局也是嵌套的,这意味着它们通过 children prop 包装子布局。您可以通过在特定的路由段(文件夹)中添加 layout 来嵌套布局。
例如,要为 /blog 路由创建一个布局,在 blog 文件夹中添加一个新的 layout 文件。

export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
return <section>{children}</section>;
}export default function BlogLayout({ children }) {
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>
);
}export default async function BlogPostPage({ params }) {
const { slug } = await params;
const post = await getPost(slug);
return (
<div>
<h1>{post.title}</h1>
<p>{post.content}</p>
</div>
);
}嵌套的 动态段中的布局 也可以访问 params props。
在一个服务器组件页面中,您可以使用 searchParams prop 访问搜索参数:
export default async function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const filters = (await searchParams).filters;
}export default async function Page({ searchParams }) {
const filters = (await searchParams).filters;
}使用 searchParams 会使您的页面选择 动态渲染,因为它需要通过传入请求来读取搜索参数。
客户端组件可以使用 useSearchParams hook 读取搜索参数。
了解更多关于 useSearchParams 在 静态渲染 和 动态渲染 路由中的应用。
searchParams prop。useSearchParams。new URLSearchParams(window.location.search) 来读取搜索参数,而不会触发重新渲染。您可以使用 <Link> 组件 在路由之间进行导航。<Link> 是一个内置的 Next.js 组件,它扩展了 HTML <a> 标签,提供了 预加载 和 客户端导航 功能。
例如,要生成博客文章列表,从 next/link 导入 <Link> 并将 href prop 传递给组件:
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>
);
}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 中路由间导航的主要方式。您也可以使用useRouterhook 进行更高级的导航。
Next.js 暴露了实用类型,可以从您的路由结构中推断 params 和命名槽:
page 组件的 Props,包括 params 和 searchParams。layout 组件的 Props,包括 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期间生成。