首页 / Next.js 16 入门教程 / App Router 基础

Next.js 16 入门教程

App Router 基础

本教程共 42 篇 · 第 3 篇 · 更新于 2026-07-30 · 约 7 分钟阅读

Next.jsNext.js 16 入门教程App Router路由layoutpage

3. App Router 基础

本节目标:搞懂 App Router 的核心思想——文件即路由,学会用 layout 和 page 搭建页面骨架。

App Router vs Pages Router

Next.js 有两套路由系统:

Pages Router(旧版)

  • pages/ 目录下创建文件
  • getServerSidePropsgetStaticProps 获取数据
  • 页面级组件直接导出

App Router(新版,推荐)

  • app/ 目录下创建文件
  • 组件默认是 Server Components,直接用 async/await 获取数据
  • layout.tsx 实现共享布局
  • 支持 React Server Components、流式渲染等新特性

本教程全程使用 App Router。Next.js 16 的所有新特性都围绕 App Router 设计。

文件即路由

App Router 的核心规则:文件夹定义路由,文件定义 UI

app/
├── page.tsx          # /
├── about/
│   └── page.tsx      # /about
└── blog/
    ├── page.tsx      # /blog
    └── [slug]/
        └── page.tsx  # /blog/:slug

每个路由段由文件夹层级决定。只有包含 page.tsx 的路径才是公开可访问的。

创建 Page

Page 是特定路由的 UI。在 app 目录里新建 page.tsx,默认导出一个 React 组件:

// app/page.tsx
export default function Page() {
  return <h1>Hello Next.js!</h1>
}
// app/about/page.tsx
export default function Page() {
  return <h1>About Us</h1>
}

Page 可以是异步组件,直接在里面获取数据:

// app/blog/page.tsx
export default async function Page() {
  const posts = await fetch('https://api.example.com/posts')
    .then(res => res.json())

  return (
    <ul>
      {posts.map((post: any) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

创建 Layout

Layout 是多个页面共享的 UI。导航时,Layout 保持状态、不重新渲染。

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <header>导航栏</header>
        <main>{children}</main>
        <footer>页脚</footer>
      </body>
    </html>
  )
}

根布局(Root Layout)是必须的,必须包含 <html><body> 标签。

children 会被替换为当前路由对应的 page 或嵌套 layout。

嵌套路由

创建嵌套路由,只需嵌套文件夹:

app/
├── layout.tsx           # 根布局
├── page.tsx             # /
└── blog/
    ├── layout.tsx       # blog 专属布局
    ├── page.tsx         # /blog
    └── [slug]/
        └── page.tsx     # /blog/:slug

访问 /blog/my-post 时,渲染顺序是:

  1. app/layout.tsx(根布局)
  2. app/blog/layout.tsx(blog 布局)
  3. app/blog/[slug]/page.tsx(文章页面)

每一层 layout 包裹下一层的输出。

嵌套 Layout

Layout 可以嵌套,子 layout 被包裹在父 layout 的 children 中:

// app/blog/layout.tsx
export default function BlogLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <section>
      <h2>博客频道</h2>
      {children}
    </section>
  )
}

最终渲染结果:

<html>
  <body>
    <header>导航栏</header>
    <main>
      <section>
        <h2>博客频道</h2>
        <!-- 这里是 page.tsx 的内容 -->
      </section>
    </main>
    <footer>页脚</footer>
  </body>
</html>

动态路由段

用方括号创建动态路由:

// 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 (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </article>
  )
}
Important

Next.js 16 写法:params 是一个 Promise,需要用 await 解包。
旧版写法(v14 及之前):params 是同步对象,直接解构。

searchParams

Page 可以接收 URL 查询参数:

// app/products/page.tsx
export default async function Page({
  searchParams,
}: {
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
  const filters = (await searchParams).filters
  // 用 filters 过滤产品列表
}

使用 searchParams 会让页面进入动态渲染模式——因为查询参数只有在请求时才能确定。

什么时候用什么

  • searchParams prop:查询参数用于加载页面数据(分页、筛选)
  • useSearchParams hook:查询参数只在客户端使用(过滤已加载的列表)

组件层级

特殊文件的渲染顺序是固定的:

layout.tsx
  └── template.tsx
      └── error.tsx
          └── loading.tsx
              └── not-found.tsx
                  └── page.tsx

这意味着:

  • layout 包裹所有子组件
  • error 捕获下方组件的渲染错误
  • loading 在子组件加载时显示
  • page 是最内层,直接面向用户

不能做的事

Layout 有几个限制:

  1. 不能访问 searchParams——因为 layout 不重新渲染,searchParams 会过期
  2. 不能访问 pathname——同理,导航后 pathname 会变
  3. 不能向 children 传数据——但可以在同一路由多次获取数据,Next.js 会自动去重

如果需要这些功能,在 Page 里用 searchParams,或者用 Client Component 配合 usePathnameuseSearchParams

小结

这一章我们掌握了 App Router 的核心概念:

  1. 文件即路由——文件夹层级对应 URL
  2. page.tsx 定义页面 UI
  3. layout.tsx 定义共享布局,不重新渲染
  4. 动态路由用 [slug]params 是 Promise
  5. searchParams 用于读取查询参数

下一章,我们来学习路由进阶技巧——路由组、并行路由和拦截路由。