app-router

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Next.js App Router Patterns

Next.js App Router 路由模式

Overview

概述

The App Router is Next.js's file-system based router built on React Server Components. It uses a
app/
directory structure where folders define routes and special files control UI behavior.
App Router是Next.js基于React Server Components构建的文件系统路由。它采用
app/
目录结构,其中文件夹定义路由,特殊文件控制UI行为。

Core File Conventions

核心文件约定

Route Files

路由文件

Each route segment is defined by a folder. Special files within folders control behavior:
FilePurpose
page.tsx
Unique UI for a route, makes route publicly accessible
layout.tsx
Shared UI wrapper, preserves state across navigations
loading.tsx
Loading UI using React Suspense
error.tsx
Error boundary for route segment
not-found.tsx
UI for 404 responses
template.tsx
Like layout but re-renders on navigation
default.tsx
Fallback for parallel routes
每个路由分段由文件夹定义。文件夹内的特殊文件控制行为:
文件用途
page.tsx
为路由提供唯一UI,使路由可公开访问
layout.tsx
共享UI包装器,在导航时保留状态
loading.tsx
使用React Suspense实现的加载UI
error.tsx
路由分段的错误边界
not-found.tsx
404响应的UI
template.tsx
类似布局但在导航时重新渲染
default.tsx
并行路由的回退方案

Folder Conventions

文件夹约定

PatternPurposeExample
folder/
Route segment
app/blog/
/blog
[folder]/
Dynamic segment
app/blog/[slug]/
/blog/:slug
[...folder]/
Catch-all segment
app/docs/[...slug]/
/docs/*
[[...folder]]/
Optional catch-all
app/shop/[[...slug]]/
/shop
or
/shop/*
(folder)/
Route group (no URL)
app/(marketing)/about/
/about
@folder/
Named slot (parallel routes)
app/@modal/login/
_folder/
Private folder (excluded)
app/_components/
模式用途示例
folder/
路由分段
app/blog/
/blog
[folder]/
动态分段
app/blog/[slug]/
/blog/:slug
[...folder]/
全捕获分段
app/docs/[...slug]/
/docs/*
[[...folder]]/
可选全捕获
app/shop/[[...slug]]/
/shop
/shop/*
(folder)/
路由组(不影响URL)
app/(marketing)/about/
/about
@folder/
命名插槽(并行路由)
app/@modal/login/
_folder/
私有文件夹(被排除)
app/_components/

Creating Routes

创建路由

Basic Route Structure

基础路由结构

To create a new route, add a folder with
page.tsx
:
app/
├── page.tsx              # / (home)
├── about/
│   └── page.tsx          # /about
└── blog/
    ├── page.tsx          # /blog
    └── [slug]/
        └── page.tsx      # /blog/:slug
要创建新路由,添加一个包含
page.tsx
的文件夹:
app/
├── page.tsx              # /(首页)
├── about/
│   └── page.tsx          # /about
└── blog/
    ├── page.tsx          # /blog
    └── [slug]/
        └── page.tsx      # /blog/:slug

Page Component

页面组件

A page is a Server Component by default:
tsx
// app/about/page.tsx
export default function AboutPage() {
  return (
    <main>
      <h1>About Us</h1>
      <p>Welcome to our company.</p>
    </main>
  )
}
页面默认是Server Component:
tsx
// app/about/page.tsx
export default function AboutPage() {
  return (
    <main>
      <h1>关于我们</h1>
      <p>欢迎来到我们的公司。</p>
    </main>
  )
}

Dynamic Routes

动态路由

Access route parameters via the
params
prop:
tsx
// app/blog/[slug]/page.tsx
interface PageProps {
  params: Promise<{ slug: string }>
}

export default async function BlogPost({ params }: PageProps) {
  const { slug } = await params
  const post = await getPost(slug)

  return <article>{post.content}</article>
}
通过
params
属性访问路由参数:
tsx
// app/blog/[slug]/page.tsx
interface PageProps {
  params: Promise<{ slug: string }>
}

export default async function BlogPost({ params }: PageProps) {
  const { slug } = await params
  const post = await getPost(slug)

  return <article>{post.content}</article>
}

Layouts

布局

Root Layout (Required)

根布局(必填)

Every app needs a root layout with
<html>
and
<body>
:
tsx
// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
每个应用都需要包含
<html>
<body>
的根布局:
tsx
// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

Nested Layouts

嵌套布局

Layouts wrap their children and preserve state:
tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div className="flex">
      <Sidebar />
      <main className="flex-1">{children}</main>
    </div>
  )
}
布局会包裹其子组件并保留状态:
tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div className="flex">
      <Sidebar />
      <main className="flex-1">{children}</main>
    </div>
  )
}

Loading and Error States

加载与错误状态

Loading UI

加载UI

Create instant loading states with Suspense:
tsx
// app/dashboard/loading.tsx
export default function Loading() {
  return <div className="animate-pulse">Loading...</div>
}
使用Suspense创建即时加载状态:
tsx
// app/dashboard/loading.tsx
export default function Loading() {
  return <div className="animate-pulse">加载中...</div>
}

Error Boundaries

错误边界

Handle errors gracefully:
tsx
// app/dashboard/error.tsx
'use client'

export default function Error({
  error,
  reset,
}: {
  error: Error
  reset: () => void
}) {
  return (
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={reset}>Try again</button>
    </div>
  )
}
优雅处理错误:
tsx
// app/dashboard/error.tsx
'use client'

export default function Error({
  error,
  reset,
}: {
  error: Error
  reset: () => void
}) {
  return (
    <div>
      <h2>出问题了!</h2>
      <button onClick={reset}>重试</button>
    </div>
  )
}

Route Groups

路由组

Organize routes without affecting URL structure:
app/
├── (marketing)/
│   ├── layout.tsx        # Marketing layout
│   ├── about/page.tsx    # /about
│   └── contact/page.tsx  # /contact
└── (shop)/
    ├── layout.tsx        # Shop layout
    └── products/page.tsx # /products
在不影响URL结构的情况下整理路由:
app/
├── (marketing)/
│   ├── layout.tsx        # 营销布局
│   ├── about/page.tsx    # /about
│   └── contact/page.tsx  # /contact
└── (shop)/
    ├── layout.tsx        # 商城布局
    └── products/page.tsx # /products

Metadata

元数据

Static Metadata

静态元数据

tsx
// app/about/page.tsx
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'About Us',
  description: 'Learn more about our company',
}
tsx
// app/about/page.tsx
import { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'About Us',
  description: 'Learn more about our company',
}

Dynamic Metadata

动态元数据

tsx
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
  const { slug } = await params
  const post = await getPost(slug)
  return { title: post.title }
}
tsx
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
  const { slug } = await params
  const post = await getPost(slug)
  return { title: post.title }
}

Key Patterns

关键模式

  1. Colocation: Keep components, tests, and styles near routes
  2. Private folders: Use
    _folder
    for non-route files
  3. Route groups: Use
    (folder)
    to organize without URL impact
  4. Parallel routes: Use
    @slot
    for complex layouts
  5. Intercepting routes: Use
    (.)
    patterns for modals
  1. 就近放置:将组件、测试和样式放在路由附近
  2. 私有文件夹:使用
    _folder
    存放非路由文件
  3. 路由组:使用
    (folder)
    在不影响URL的情况下整理路由
  4. 并行路由:使用
    @slot
    实现复杂布局
  5. 拦截路由:使用
    (.)
    模式实现模态框

Resources

资源

For detailed patterns, see:
  • references/routing-conventions.md
    - Complete file conventions
  • references/layouts-templates.md
    - Layout composition patterns
  • references/loading-error-states.md
    - Suspense and error handling
  • examples/dynamic-routes.md
    - Dynamic routing examples
  • examples/parallel-routes.md
    - Parallel and intercepting routes
如需了解详细模式,请查看:
  • references/routing-conventions.md
    - 完整文件约定
  • references/layouts-templates.md
    - 布局组合模式
  • references/loading-error-states.md
    - Suspense与错误处理
  • examples/dynamic-routes.md
    - 动态路由示例
  • examples/parallel-routes.md
    - 并行与拦截路由示例