首页 / Next.js 16 入门教程 / 数据获取(下)

Next.js 16 入门教程

数据获取(下)

本教程共 42 篇 · 第 9 篇 · 更新于 2026-07-30 · 约 7 分钟阅读

Next.jsNext.js 16 入门教程paramssearchParams缓存unstable_cache

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
}>

值可能是 stringstring[](同名参数多次出现)、或 undefined(参数不存在)。

使用 searchParams 的后果

使用 searchParams 会让页面进入动态渲染模式。因为查询参数只有在请求时才能确定,页面无法在构建时预渲染。

如果不需要动态行为,考虑:

  1. searchParams 传给 Client Component 处理
  2. useSearchParams hook 在客户端读取

缓存控制

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 控制缓存的有效期,内置了 secondsminuteshoursdaysweeksmax 等预设,也支持自定义 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()
  • searchParams
  • params(没有 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,直到缓存失效。

小结

这一章我们学了数据获取的进阶技巧:

  1. paramssearchParams 是 Promise,需要 await
  2. Client Component 用 use(params)useParams() 读取
  3. use cache 指令缓存数据或 UI
  4. cacheLife 控制缓存有效期
  5. cacheTag 给缓存打标签,配合 revalidateTag 清除
  6. 运行时 API 需要用 <Suspense> 包裹

下一章,我们来学习数据变更——Server Actions。