前端开发

Next.js App Router 完全指南

2024-01-20
12 分钟

Next.js App Router 完全指南

Next.js 13 引入了全新的 App Router,基于 React Server Components 构建,为我们提供了更强大和灵活的路由系统。

App Router 基础

文件系统路由

App Router 使用文件系统来定义路由,每个文件夹代表一个路由段。

```
app/
├── page.tsx // /
├── about/
│ └── page.tsx // /about
├── blog/
│ ├── page.tsx // /blog
│ └── [slug]/
│ └── page.tsx // /blog/[slug]
└── dashboard/
├── layout.tsx
├── page.tsx // /dashboard
└── settings/
└── page.tsx // /dashboard/settings
```

特殊文件

App Router 定义了几个特殊文件:

  • page.tsx: 定义路由的 UI
  • layout.tsx: 定义共享的 UI 布局
  • loading.tsx: 加载状态 UI
  • error.tsx: 错误状态 UI
  • not-found.tsx: 404 页面

布局系统

根布局

每个应用都需要一个根布局:

```tsx
// app/layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (





{children}

页脚



)
}
```

嵌套布局

可以为特定路由段创建嵌套布局:

```tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (



{children}


)
}
```

数据获取

Server Components

默认情况下,App Router 中的组件都是 Server Components:

```tsx
// app/blog/page.tsx
async function getBlogPosts() {
const res = await fetch('https://api.example.com/posts')
return res.json()
}

export default async function BlogPage() {
const posts = await getBlogPosts()

return (


博客文章


{posts.map(post => (

{post.title}


{post.excerpt}



))}

)
}
```

并行数据获取

可以并行获取多个数据源:

```tsx
async function getUser(id: string) {
const res = await fetch(/api/users/${id})
return res.json()
}

async function getUserPosts(id: string) {
const res = await fetch(/api/users/${id}/posts)
return res.json()
}

export default async function UserPage({ params }: { params: { id: string } }) {
// 并行获取数据
const [user, posts] = await Promise.all([
getUser(params.id),
getUserPosts(params.id)
])

return (


{user.name}



{posts.map(post => (
{post.title}

))}


)
}
```

动态路由

基本动态路由

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

```tsx
// app/blog/[slug]/page.tsx
export default function BlogPost({ params }: { params: { slug: string } }) {
return

文章: {params.slug}


}
```

捕获所有路由

使用 [...slug] 捕获所有路由段:

```tsx
// app/docs/[...slug]/page.tsx
export default function DocsPage({ params }: { params: { slug: string[] } }) {
return

文档路径: {params.slug.join('/')}


}
```

路由组

使用括号创建路由组,不影响 URL 结构:

```
app/
├── (marketing)/
│ ├── about/
│ │ └── page.tsx
│ └── contact/
│ └── page.tsx
└── (dashboard)/
├── analytics/
│ └── page.tsx
└── settings/
└── page.tsx
```

中间件

在根目录创建 middleware.ts 文件:

```tsx
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
// 检查认证
const token = request.cookies.get('token')

if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}

return NextResponse.next()
}

export const config = {
matcher: ['/dashboard/:path*']
}
```

最佳实践

1. 合理使用 Server Components

默认使用 Server Components,只在需要交互时使用 Client Components:

```tsx
// Server Component (默认)
async function ServerComponent() {
const data = await fetchData()
return

{data}

}

// Client Component (需要 'use client')
'use client'
function ClientComponent() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}
}
```

2. 优化数据获取

使用 fetch 的缓存功能:

```tsx
// 缓存数据
async function getData() {
const res = await fetch('https://api.example.com/data', {
cache: 'force-cache' // 强制缓存
})
return res.json()
}

// 重新验证数据
async function getRealtimeData() {
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 60 } // 60秒后重新验证
})
return res.json()
}
```

3. 错误处理

创建错误边界:

```tsx
// app/error.tsx
'use client'

export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (


出错了!


<button onClick={() => reset()}>重试

)
}
```

总结

App Router 为 Next.js 应用带来了:

  • 更好的性能: Server Components 减少了客户端 JavaScript
  • 更灵活的布局: 嵌套布局和路由组
  • 更简单的数据获取: 直接在组件中使用 async/await
  • 更好的开发体验: 内置的加载和错误状态

掌握这些概念和最佳实践,你就能充分利用 App Router 的强大功能!
```