数据获取
数据获取是任何应用程序的核心部分。本页将介绍使用你喜欢的方法获取数据的最佳实践。
决定是在服务器还是客户端获取数据取决于你正在构建的 UI 类型。
对于大多数情况,当你不需要实时数据 (例如轮询) 时,你可以使用 服务器组件 在服务器上获取数据。这样做有几个好处:
- 你可以在单次服务器往返中获取数据,减少网络请求数量和客户端-服务器瀑布。
- 防止敏感信息 (如访问令牌和 API 密钥) 暴露给客户端 (这将需要中间 API 路由)。
- 通过在靠近数据源的地方获取数据来减少延迟 (如果你的应用程序代码和数据库在同一区域)。
- 数据请求可以被 缓存 和 重新验证。
然而,服务器端数据获取将导致整个页面在服务器上重新渲染。在需要变更/重新验证较小的 UI 片段或持续获取实时数据 (例如实时视图) 的情况下,客户端数据获取可能更合适,因为它允许你在客户端重新渲染特定的 UI 片段。
在 Next.js 中有 4 种获取数据的方式:
Next.js 扩展了原生的 fetch
Web API,允许你为服务器上的每个获取请求配置 缓存 和 重新验证 行为。你可以在 服务器组件、路由处理程序 和 服务器操作 中使用 fetch
。例如:
默认情况下,fetch
请求会获取新数据。使用它将导致整个路由被 动态渲染,并且数据不会被缓存。
你可以通过设置 cache
选项为 force-cache
来缓存 fetch
请求。这意味着数据将被缓存,并且组件将被 静态渲染:
或者,如果使用 PPR,我们建议将使用 fetch
请求的组件包装在 Suspense 边界中。这将确保只有使用 fetch
的组件被动态渲染和流式传输,而不是整个页面:
有关更多信息,请参阅 缓存和重新验证 文档。
值得注意的是:在 Next.js 14 及更早版本中,fetch
请求默认被缓存。有关更多信息,请参阅 升级指南。
如果你需要在组件树中的多个组件中获取相同的数据,你不必全局获取数据并通过 props 传递下去。相反,你可以在需要数据的组件中获取数据,而不必担心多次请求相同数据的性能影响。
这是可能的,因为在 React 渲染过程中,具有相同 URL 和选项的 fetch
请求会自动被记忆。
了解更多关于 请求记忆 的信息。
你可以在 服务器组件、路由处理程序 和 服务器操作 中调用你的 ORM 或数据库客户端。
你可以使用 React cache
来在 React 渲染过程中记忆数据请求。例如,虽然 getItem
函数在布局和页面中都被调用,但只会对数据库进行一次查询:
你也可以使用实验性的 unstable_cache
和 unstable_noStore
API 来配置这些请求的缓存和重新验证行为。
你可以在客户端组件中使用数据获取库,如 SWR 或 React Query 来获取数据。这些库提供了自己的 API 用于缓存、重新验证和变更数据。
例如,使用 SWR 在客户端定期获取数据:
如果你需要创建 API 端点,Next.js 支持 路由处理程序。路由处理程序在服务器上执行,防止敏感信息 (例如 API 凭证) 暴露给客户端。
例如,使用 SWR 调用一个路由处理程序:
查看 路由处理程序 文档以获取更多示例。
值得注意的是:由于服务器组件在服务器上渲染,你不需要从服务器组件调用路由处理程序。你可以直接访问你的数据。
在组件内获取数据时,你需要注意两种数据获取模式:并行和顺序。
- 顺序:组件树中的请求相互依赖。这可能导致较长的加载时间。
- 并行:路由中的请求被急切地发起,并将同时加载数据。这减少了加载数据所需的总时间。
如果你有嵌套组件,并且每个组件都获取自己的数据,那么如果这些数据请求没有被 记忆,数据获取将按顺序进行。
可能有一些情况下你想要这种模式,因为一个获取依赖于另一个的结果。例如,Playlists
组件只有在 Artist
组件完成数据获取后才会开始获取数据,因为 Playlists
依赖于 artistID
属性:
你可以使用 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
函数: