数据获取与缓存
示例
本指南将介绍 Next.js 中数据获取和缓存的基础知识,提供实用示例和最佳实践。
这是一个最简单的 Next.js 数据获取示例:
这个示例演示了在异步 React 服务器组件中使用 fetch
API 进行基本的服务端数据获取。
这个组件将获取并显示博客文章列表。来自 fetch
的响应将被自动缓存。
如果你在这个路由的其他地方没有使用任何动态 API,它将在 next build
时被预渲染为静态页面。然后可以使用增量静态再生成更新数据。
如果你不想缓存来自 fetch
的响应,你可以这样做:
这个组件将获取并显示博客文章列表。来自数据库的响应默认不会被缓存,但可以通过额外配置实现缓存。
如果你在这个路由的其他地方没有使用任何动态 API,它将在 next build
时被预渲染为静态页面。然后可以使用增量静态再生成更新数据。
要阻止页面预渲染,你可以在文件中添加以下内容:
但是,你通常会使用 cookies
、headers
或从页面 props 读取传入的 searchParams
等函数,这些会自动使页面动态渲染。在这种情况下,你不需要显式使用 force-dynamic
。
我们建议首先尝试在服务端获取数据。
然而,在某些情况下,客户端数据获取是有意义的。在这些场景中,你可以在 useEffect
中手动调用 fetch
(不推荐),或者使用社区中流行的 React 库(如 SWR 或 React Query)进行客户端获取。
你可以使用 unstable_cache
API 来缓存响应,允许页面在运行 next build
时被预渲染。
这个示例将数据库查询的结果缓存 1 小时(3600 秒)。它还添加了缓存标签 posts
,可以通过增量静态再生成进行失效处理。
Next.js 使用了像 generateMetadata
和 generateStaticParams
这样的 API,在这些情况下,你需要使用在 page
中获取的相同数据。
如果你使用 fetch
,请求会自动被记忆化。这意味着你可以安全地使用相同的 URL 和相同的选项调用多次,只会发出一个请求。
如果你不使用 fetch
,而是直接使用 ORM 或数据库,你可以用 React cache
函数包装你的数据获取。这将去重并只进行一次查询。
了解更多关于使用增量静态再生成重新验证缓存数据。
在组件中获取数据时,你需要了解两种数据获取模式:并行和顺序。
- 顺序:组件树中的请求相互依赖。这可能导致加载时间更长。
- 并行:路由中的请求会立即开始,并且会同时加载数据。这减少了加载数据所需的总时间。
如果你有嵌套组件,且每个组件都获取自己的数据,那么如果这些数据请求没有被记忆化,数据获取将按顺序进行。
在某些情况下,你可能需要这种模式,因为一个获取依赖于另一个的结果。例如,Playlists
组件只有在 Artist
组件完成数据获取后才会开始获取数据,因为 Playlists
依赖于 artistID
prop:
你可以使用 loading.js
(用于路由段)或 React <Suspense>
(用于嵌套组件)来显示即时加载状态,同时 React 流式传输结果。
这将防止整个路由被数据请求阻塞,用户将能够与页面中准备就绪的部分进行交互。
默认情况下,布局和页面段会并行渲染。这意味着请求会并行发起。
然而,由于 async
/await
的特性,同一段或组件中的一个被等待的请求会阻塞其下的请求。
要并行获取数据,你可以通过在组件外部定义请求来提前发起它们。这通过并行发起两个请求来节省时间,但是用户在两个 Promise 都解决之前不会看到渲染结果。
在下面的示例中,getArtist
和 getAlbums
函数在 Page
组件外部定义,并在组件内部使用 Promise.all
发起:
此外,你可以添加一个 Suspense 边界 来分解渲染工作,并尽快显示部分结果。
防止瀑布流的另一种方法是使用预加载模式,即创建一个你可以在阻塞请求之前提前调用的工具函数。例如,checkIsAvailable()
阻塞了 <Item/>
的渲染,所以你可以在它之前调用 preload()
来提前开始 <Item/>
的数据依赖。当 <Item/>
被渲染时,它的数据已经被获取了。
注意 preload
函数不会阻塞 checkIsAvailable()
的执行。
值得注意的是: "preload" 函数可以有任何名称,因为它是一个模式,而不是 API。
你可以将 cache
函数、预加载模式和 server-only
包结合起来,创建一个可以在整个应用中使用的数据获取工具。
使用这种方法,你可以提前获取数据、缓存响应,并确保这种数据获取只发生在服务器上。
布局、页面或其他组件可以使用 utils/get-item
导出的函数,使它们能够控制项目数据的获取时机。
值得注意的是:
我们推荐使用 React 的污染 API,taintObjectReference
和 taintUniqueValue
,来防止整个对象实例或敏感值被传递到客户端。
要在你的应用中启用污染功能,将 Next.js 配置中的 experimental.taint
选项设置为 true
:
然后将你想要污染的对象或值传递给 experimental_taintObjectReference
或 experimental_taintUniqueValue
函数: