Next.js 使用基于文件系统的路由,这意味着您可以使用文件夹和文件来定义路由。本页面将指导您如何创建布局和页面,以及如何在它们之间建立链接

创建 Page

页面是在特定路由上渲染的 UI。要创建页面,请在  app  目录中添加一个  page  文件,并默认导出一个 React 组件。例如,要创建索引页 (/):

创建 Layout

布局是多个页面之间共享的 UI。导航时,布局会保留状态、保持交互性,并且不会重新渲染

你可以通过从 layout  文件默认导出 React 组件来定义布局。该组件应该接受  children  属性,该属性可以是页面或其他布局

例如,要创建接受索引页作为子页的布局,请在  app  目录中添加  layout  文件:

// app/layout.tsx
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  标签

创建嵌套路线 (nested route)

嵌套路由是由多个 URL 段组成的路由。例如,/blog/[slug]  路由由三个段组成:

  • / (Root Segment)
  • blog (Segment)
  • [slug] (Leaf Segment)

在 Next.js 中:

  • 文件夹用于定义映射到 URL 段的路由段
  • 文件(如  page  和  layout)用于创建为某个片段显示的 UI

要创建嵌套路由,您可以将文件夹嵌套在一起。例如,要为  /blog  添加路由,请在  app  目录中创建一个名为  blog  文件夹。然后,要使  /blog  可公开访问,请添加一个  page.tsx  文件

您可以继续嵌套文件夹来创建嵌套路由。例如,要为特定博客文章创建路由,请在  blog  中创建一个新的  [slug]  文件夹并添加一个  page  文件

将文件夹名称括在方括号中(例如  [slug])会创建一个动态路由段,用于根据数据生成多个页面,例如博客文章、产品页面等

嵌套布局 (Nesting layouts)

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

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

如果您要组合上述两个布局,根布局 (app/layout.js) 将包装博客布局 (app/blog/layout.js),后者将包装博客 (app/blog/page.js) 和博客文章页面 (app/blog/[slug]/page.js)

创建动态路由段 (dynamic segment)

动态路由段允许您创建基于数据生成的路由。例如,您无需为每篇博客文章手动创建路由,而是可以创建一个动态路由段,根据博客文章数据生成路由

要创建动态路由段,请将片段(文件夹)名称括在方括号中:[segmentName]。例如,在  app/blog/[slug]/page.tsx  路由中, [slug]  就是动态路由段

// app/blog/[slug]/page.tsx
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>
  )
}

动态路由段的 layout.js 等文件都是能获取到 params props 的

Rendering with search params

在服务器组件 Page 中,可以使用  searchParams  属性访问搜索参数:

// app/page.tsx
export default async function Page({ searchParams }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) {
  const filters = (await searchParams).filters
}

使用  searchParams  会使您的页面进入动态渲染,因为它需要传入请求来读取搜索参数

客户端组件可以使用  useSearchParams  钩子读取搜索参数

何时使用?

  • 当您需要搜索参数来加载页面数据(例如分页、从数据库过滤)时
  • 当搜索参数仅在客户端使用时使用  useSearchParams(例如,过滤已通过 props 加载的列表)
  • 作为一项小优化,您可以在回调或事件处理程序中使用  new URLSearchParams(window.location.search)  来读取搜索参数,而无需触发重新渲染

页面之间的链接

您可以使用  <Link>  组件在路由之间导航。<Link>  是一个内置的 Next.js 组件,它扩展了 HTML <a>  标签以提供预取客户端导航

例如,要生成博客文章列表,请从  next/link  导入  <Link> ,并将  href  属性传递给组件:

// app/ui/post.tsx
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  钩子进行更高级的导航