数据获取(下)
本教程共 42 篇 · 第 9 篇 · 更新于 2026-07-30 · 约 7 分钟阅读
9. 数据获取(下)
本节目标:掌握 params/searchParams 的异步化用法,学会用缓存指令控制数据生命周期。
params 异步化
Next.js 15+ 开始,params 变成了 Promise,需要 await 解包:
// Next.js 16 写法
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await getPost(slug)
return <div>{post.title}</div>
}
Important旧版写法(v14 及之前):
params是同步对象,直接{ slug } = params。
Next.js 15 为了兼容保留了同步访问,但已废弃。Next.js 16 必须用await。
Layout 中的 params
Layout 也能接收 params:
export default async function Layout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ team: string }>
}) {
const { team } = await params
return (
<section>
<h1>{team} 的团队</h1>
<main>{children}</main>
</section>
)
}
Client Component 中读取 params
Client Component 不能用 async,需要用 React 的 use API:
'use client'
import { use } from 'react'
export default function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = use(params)
return <div>{slug}</div>
}
或者用 useParams Hook:
'use client'
import { useParams } from 'next/navigation'
export default function Page() {
const params = useParams<{ slug: string }>()
return <div>{params.slug}</div>
}
searchParams 异步化
和 params 一样,searchParams 也是 Promise:
export default async function Page({
searchParams,
}: {
searchParams: Promise<{
[key: string]: string | string[] | undefined
}>
}) {
const { q, sort } = await searchParams
return (
<div>
<p>搜索:{q}</p>
<p>排序:{sort}</p>
</div>
)
}
searchParams 的类型
type SearchParams = Promise<{
[key: string]: string | string[] | undefined
}>
值可能是 string、string[](同名参数多次出现)、或 undefined(参数不存在)。
使用 searchParams 的后果
使用 searchParams 会让页面进入动态渲染模式。因为查询参数只有在请求时才能确定,页面无法在构建时预渲染。
如果不需要动态行为,考虑:
- 把
searchParams传给 Client Component 处理 - 用
useSearchParamshook 在客户端读取
缓存控制
Next.js 16 引入了新的缓存模型——Cache Components。
启用 Cache Components
在 next.config.ts 中开启:
const nextConfig: NextConfig = {
cacheComponents: true,
}
export default nextConfig
use cache 指令
用 "use cache" 指令缓存函数或组件的返回值:
数据级缓存
import { cacheLife } from 'next/cache'
export async function getProducts() {
'use cache'
cacheLife('hours') // 缓存 1 小时
return db.query('SELECT * FROM products')
}
UI 级缓存
export default async function Page() {
'use cache'
cacheLife('hours')
const users = await db.query('SELECT * FROM users')
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
}
cacheLife 配置
cacheLife 控制缓存的有效期,内置了 seconds、minutes、hours、days、weeks、max 等预设,也支持自定义 stale/revalidate/expire 三个时间维度。完整预设表与自定义配置见 §11。
cacheTag 标签
给缓存打标签,方便按需清除:
export async function getProducts() {
'use cache'
cacheTag('products')
return db.query('SELECT * FROM products')
}
之后可以通过 revalidateTag('products', 'max') 清除所有带这个标签的缓存。
运行时 API 的处理
以下 API 依赖请求时的信息,不能直接用于缓存的组件:
cookies()headers()searchParamsparams(没有generateStaticParams时)
如果组件用了这些 API,必须用 <Suspense> 包裹:
import { cookies } from 'next/headers'
import { Suspense } from 'react'
async function UserGreeting() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')?.value || 'light'
return <p>你的主题:{theme}</p>
}
export default function Page() {
return (
<>
<h1>仪表盘</h1>
<Suspense fallback={<p>加载中...</p>}>
<UserGreeting />
</Suspense>
</>
)
}
把运行时值传给缓存组件
运行时值(如 session)可以先在非缓存组件中提取,再作为参数传给缓存组件,让它成为缓存 key 的一部分。具体示例见 §11。
非确定性操作
Math.random()、Date.now() 这些每次执行结果不同的操作,在缓存组件里需要特殊处理。
每次请求生成新值
import { connection } from 'next/server'
async function UniqueContent() {
await connection() // 标记为动态
const uuid = crypto.randomUUID()
return <p>请求 ID:{uuid}</p>
}
缓存同一个值
export default async function Page() {
'use cache'
const buildId = crypto.randomUUID()
return <p>构建 ID:{buildId}</p>
}
所有用户看到相同的 buildId,直到缓存失效。
小结
这一章我们学了数据获取的进阶技巧:
params和searchParams是 Promise,需要await- Client Component 用
use(params)或useParams()读取 use cache指令缓存数据或 UIcacheLife控制缓存有效期cacheTag给缓存打标签,配合revalidateTag清除- 运行时 API 需要用
<Suspense>包裹
下一章,我们来学习数据变更——Server Actions。