⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议
Skip to content

2.1.2 文件目录就是网页路由——App Router 架构

本质还原

App Router 的本质是文件系统到 URL 的映射。创建一个文件夹,就是创建一个路由段;在文件夹中放入 page.tsx,这个路由就能被访问。

传统路由:在代码中配置 URL → 组件的映射
App Router:文件夹结构 = URL 结构

可视化解构:目录结构与 URL

核心约定文件

App Router 中的每个文件夹可以包含以下约定文件:

文件名作用是否必需
page.tsx路由的 UI 内容路由可访问则必需
layout.tsx共享布局,子路由复用可选
loading.tsx加载状态 UI可选
error.tsx错误处理 UI可选
not-found.tsx404 页面可选
route.tsAPI 端点与 page.tsx 互斥

最小目录结构

app/
├── layout.tsx      # 根布局(必需)
├── page.tsx        # 首页
├── globals.css     # 全局样式
└── about/
    └── page.tsx    # /about 页面

嵌套布局:最强大的特性

布局是 App Router 最强大的特性之一。它允许你在不同层级共享 UI,且切换路由时布局不会重新渲染

布局代码示例

typescript
// app/layout.tsx - 根布局
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="zh">
      <body>
        <Header />
        {children}  {/* 子路由内容插入这里 */}
        <Footer />
      </body>
    </html>
  )
}

// app/dashboard/layout.tsx - Dashboard 布局
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div className="flex">
      <Sidebar />
      <main className="flex-1">{children}</main>
    </div>
  )
}

动态路由

使用方括号 [] 创建动态路由段:

语法示例文件匹配 URL
[slug]blog/[slug]/page.tsx/blog/hello
[...slug]docs/[...slug]/page.tsx/docs/a/b/c
[[...slug]]shop/[[...slug]]/page.tsx/shop/shop/a/b
typescript
// app/blog/[slug]/page.tsx
export default function BlogPost({
  params,
}: {
  params: { slug: string }
}) {
  return <h1>文章: {params.slug}</h1>
}

路由组:组织而不影响 URL

使用圆括号 () 创建路由组,用于组织代码但不影响 URL:

app/
├── (marketing)/        # 不会出现在 URL 中
│   ├── layout.tsx      # marketing 页面共享的布局
│   ├── about/
│   │   └── page.tsx    # URL: /about
│   └── contact/
│       └── page.tsx    # URL: /contact
└── (shop)/             # 不会出现在 URL 中
    ├── layout.tsx      # shop 页面共享的布局
    └── products/
        └── page.tsx    # URL: /products

觉知:Review AI 代码时的检查点

1. page.tsx 位置正确吗?

app/
├── dashboard/
│   └── settings/
│       └── page.tsx    # ✅ /dashboard/settings

├── dashboard/
│   └── settings.tsx    # ❌ 这不是页面,是普通组件

2. layout.tsx 用对了吗?

typescript
// ❌ 错误:layout 不应该有状态
export default function Layout({ children }) {
  const [user, setUser] = useState()  // 不要这样做
  return <div>{children}</div>
}

// ✅ 正确:状态应该在 Client Component 中
export default function Layout({ children }) {
  return (
    <div>
      <UserProvider>  {/* 用 Context Provider */}
        {children}
      </UserProvider>
    </div>
  )
}

3. 动态路由参数类型正确吗?

typescript
// AI 可能忘记给 params 加类型
export default function Page({ params }) {  // ❌ 缺少类型
  // ...
}

export default function Page({ 
  params 
}: { 
  params: { slug: string }  // ✅ 明确类型
}) {
  // ...
}

本节小结

App Router 的核心理念:约定优于配置

概念作用
文件系统路由文件夹结构 = URL 结构
嵌套布局UI 复用,切换不重渲染
约定文件page/layout/loading/error
动态路由[param] 语法
路由组(group) 组织代码