Next.js数据获取基础 #
一、数据获取概述 #
1.1 获取方式 #
Next.js 提供了多种数据获取方式:
| 方式 | 适用场景 | 执行位置 |
|---|---|---|
| fetch API | 服务端组件 | 服务端 |
| ORM/数据库 | 服务端组件 | 服务端 |
| 第三方库 | 服务端/客户端 | 取决于库 |
| SWR/React Query | 客户端组件 | 客户端 |
1.2 扩展fetch #
Next.js 扩展了原生 fetch API,增加了缓存和重新验证功能:
tsx
async function getData() {
const res = await fetch('https://api.example.com/data', {
cache: 'force-cache',
})
return res.json()
}
二、fetch缓存选项 #
2.1 自动缓存 #
默认情况下,fetch 请求会被缓存:
tsx
async function getData() {
const res = await fetch('https://api.example.com/data')
return res.json()
}
等效于:
tsx
async function getData() {
const res = await fetch('https://api.example.com/data', {
cache: 'force-cache',
})
return res.json()
}
2.2 禁用缓存 #
tsx
async function getData() {
const res = await fetch('https://api.example.com/data', {
cache: 'no-store',
})
return res.json()
}
2.3 定时重新验证 #
tsx
async function getData() {
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 60 },
})
return res.json()
}
2.4 缓存选项对比 #
| 选项 | 说明 | 适用场景 |
|---|---|---|
cache: 'force-cache' |
永久缓存 | 静态数据 |
cache: 'no-store' |
不缓存 | 实时数据 |
next: { revalidate: 60 } |
60秒后重新验证 | 半动态数据 |
三、请求去重 #
3.1 自动去重 #
Next.js 自动对相同的请求进行去重:
tsx
async function getUser() {
const res = await fetch('https://api.example.com/user')
return res.json()
}
export default async function Page() {
const user1 = await getUser()
const user2 = await getUser()
return <div>{user1.name}</div>
}
上述代码只会发送一次请求。
3.2 去重原理 #
Next.js 使用请求 URL 和选项作为缓存键:
tsx
fetch('https://api.example.com/data', {
headers: { 'Authorization': 'Bearer token' },
})
不同的 headers 会产生不同的缓存。
3.3 AbortController #
tsx
async function getData() {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 5000)
try {
const res = await fetch('https://api.example.com/data', {
signal: controller.signal,
})
return res.json()
} finally {
clearTimeout(timeoutId)
}
}
四、静态数据获取 #
4.1 构建时获取 #
tsx
async function getStaticData() {
const res = await fetch('https://api.example.com/static-data', {
cache: 'force-cache',
})
return res.json()
}
export default async function Page() {
const data = await getStaticData()
return <div>{data.content}</div>
}
4.2 generateStaticParams #
tsx
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(res => res.json())
return posts.map(post => ({
slug: post.slug,
}))
}
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params
const post = await fetch(`https://api.example.com/posts/${slug}`).then(res => res.json())
return <article>{post.content}</article>
}
五、动态数据获取 #
5.1 每次请求获取 #
tsx
async function getDynamicData() {
const res = await fetch('https://api.example.com/dynamic-data', {
cache: 'no-store',
})
return res.json()
}
export default async function Page() {
const data = await getDynamicData()
return <div>{data.content}</div>
}
5.2 动态函数 #
使用动态函数会自动禁用缓存:
tsx
import { cookies, headers } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
const headersList = await headers()
const token = cookieStore.get('token')
const userAgent = headersList.get('user-agent')
const data = await fetch('https://api.example.com/data', {
headers: { Authorization: `Bearer ${token}` },
}).then(res => res.json())
return <div>{data.content}</div>
}
5.3 动态配置 #
tsx
export const dynamic = 'force-dynamic'
export default async function Page() {
const data = await fetch('https://api.example.com/data').then(res => res.json())
return <div>{data.content}</div>
}
六、增量静态再生 #
6.1 基于时间的重新验证 #
tsx
async function getData() {
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 60 },
})
return res.json()
}
export default async function Page() {
const data = await getData()
return <div>{data.content}</div>
}
6.2 按需重新验证 #
基于标签
tsx
async function getData() {
const res = await fetch('https://api.example.com/data', {
next: { tags: ['collection'] },
})
return res.json()
}
触发重新验证:
tsx
import { revalidateTag } from 'next/cache'
export async function POST() {
revalidateTag('collection')
return Response.json({ revalidated: true })
}
基于路径
tsx
import { revalidatePath } from 'next/cache'
export async function POST() {
revalidatePath('/blog')
return Response.json({ revalidated: true })
}
七、错误处理 #
7.1 基本错误处理 #
tsx
export default async function Page() {
try {
const res = await fetch('https://api.example.com/data')
if (!res.ok) {
throw new Error('Failed to fetch data')
}
const data = await res.json()
return <div>{data.content}</div>
} catch (error) {
return <div>加载失败</div>
}
}
7.2 重试机制 #
tsx
async function fetchWithRetry(url: string, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const res = await fetch(url)
if (res.ok) return res.json()
} catch (error) {
if (i === retries - 1) throw error
}
}
}
7.3 超时处理 #
tsx
async function fetchWithTimeout(url: string, timeout = 5000) {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)
try {
const res = await fetch(url, { signal: controller.signal })
return res.json()
} finally {
clearTimeout(timeoutId)
}
}
八、最佳实践 #
8.1 封装API请求 #
tsx
const API_BASE = process.env.NEXT_PUBLIC_API_URL
interface FetchOptions extends RequestInit {
cache?: 'force-cache' | 'no-store'
revalidate?: number
tags?: string[]
}
export async function api<T>(
endpoint: string,
options: FetchOptions = {}
): Promise<T> {
const { cache, revalidate, tags, ...fetchOptions } = options
const url = `${API_BASE}${endpoint}`
const res = await fetch(url, {
...fetchOptions,
cache,
next: revalidate || tags ? { revalidate, tags } : undefined,
})
if (!res.ok) {
throw new Error(`API Error: ${res.status}`)
}
return res.json()
}
8.2 类型安全 #
tsx
interface User {
id: string
name: string
email: string
}
interface Post {
id: string
title: string
content: string
author: User
}
export async function getUser(id: string): Promise<User> {
return api<User>(`/users/${id}`)
}
export async function getPosts(): Promise<Post[]> {
return api<Post[]>('/posts', { next: { revalidate: 60 } })
}
8.3 并行请求 #
tsx
export default async function Page() {
const [users, posts, comments] = await Promise.all([
fetch('https://api.example.com/users').then(res => res.json()),
fetch('https://api.example.com/posts').then(res => res.json()),
fetch('https://api.example.com/comments').then(res => res.json()),
])
return (
<div>
<UsersList users={users} />
<PostsList posts={posts} />
<CommentsList comments={comments} />
</div>
)
}
九、总结 #
数据获取基础要点:
| 要点 | 说明 |
|---|---|
| fetch扩展 | 缓存和重新验证 |
| 缓存选项 | force-cache/no-store/revalidate |
| 请求去重 | 自动去重相同请求 |
| 静态获取 | 构建时获取数据 |
| 动态获取 | 每次请求获取数据 |
| ISR | 增量静态再生 |
下一步,让我们学习Server Components数据获取!
最后更新:2026-03-28