如何获取数据并实现流式传输
本页将引导您了解如何在服务端与客户端组件中获取数据,以及如何流式传输依赖数据的组件。
数据获取
服务端组件
您可以通过以下方式在服务端组件中获取数据:
使用 fetch
API
要通过 fetch
API 获取数据,将组件转换为异步函数并等待 fetch
调用。例如:
须知:
fetch
响应默认不会被缓存。但 Next.js 会预渲染路由,输出会被缓存以提高性能。如需启用动态渲染,请使用{ cache: 'no-store' }
选项。详见fetch
API 参考。- 开发过程中,您可以记录
fetch
调用以便更好地调试和观察。参见logging
API 参考。
使用 ORM 或数据库
由于服务端组件在服务器上渲染,您可以安全地使用 ORM 或数据库客户端进行查询。将组件转换为异步函数并等待调用:
客户端组件
在客户端组件中有两种获取数据的方式:
- 使用 React 的
use
hook - 使用社区库如 SWR 或 React Query
使用 use
hook 流式传输数据
您可以使用 React 的 use
hook 将数据从服务器流式传输到客户端。首先在服务端组件中获取数据,然后将 Promise 作为 prop 传递给客户端组件:
然后在客户端组件中使用 use
hook 读取 Promise:
在上面的示例中,<Posts>
组件被包裹在 <Suspense>
边界中。这意味着在 Promise 解析期间会显示 fallback 内容。了解更多关于流式传输的内容。
社区库
您可以使用社区库如 SWR 或 React Query 在客户端组件中获取数据。这些库有自己的缓存、流式传输和其他特性的语义。例如,使用 SWR:
使用 React.cache
去重请求
去重是指在渲染过程中防止对同一资源发出重复请求的过程。它允许您在不同的组件中获取相同的数据,同时防止向数据源发出多个网络请求。
如果使用 fetch
,可以通过添加 cache: 'force-cache'
来去重请求。这意味着您可以安全地使用相同的 URL 和选项调用,只会发出一个请求。
如果不使用 fetch
,而是直接使用 ORM 或数据库,可以使用 React cache
函数包装数据获取。
流式传输
警告: 以下内容假设您的应用程序启用了
dynamicIO
配置选项。该标志在 Next.js 15 canary 版本中引入。
在服务端组件中使用 async/await
时,Next.js 会启用动态渲染。这意味着数据将在服务器上为每个用户请求获取和渲染。如果有任何慢速数据请求,整个路由的渲染将被阻塞。
为了改善初始加载时间和用户体验,您可以使用流式传输将页面的 HTML 分成较小的块,并逐步将这些块从服务器发送到客户端。

有两种方式可以在应用中实现流式传输:
- 使用
loading.js
文件包裹页面 - 使用
<Suspense>
包裹组件
使用 loading.js
您可以在页面所在文件夹中创建 loading.js
文件,以便在获取数据时流式传输整个页面。例如,要流式传输 app/blog/page.js
,请在 app/blog
文件夹中添加该文件。

在导航时,用户会立即看到布局和加载状态,同时页面正在渲染。一旦渲染完成,新内容会自动替换。

在幕后,loading.js
将被嵌套在 layout.js
中,并自动将 page.js
文件及其子内容包裹在 <Suspense>
边界中。

这种方法适用于路由段(布局和页面),但对于更细粒度的流式传输,可以使用 <Suspense>
。
使用 <Suspense>
<Suspense>
允许您更精细地控制页面的哪些部分需要流式传输。例如,您可以立即显示 <Suspense>
边界外的任何页面内容,并流式传输边界内的博客文章列表。
创建有意义的加载状态
即时加载状态是在导航后立即向用户显示的 fallback UI。为了获得最佳用户体验,我们建议设计有意义的加载状态,帮助用户理解应用正在响应。例如,可以使用骨架屏和旋转器,或未来屏幕的一小部分但有意义的内容,如封面照片、标题等。
在开发过程中,您可以使用 React Devtools 预览和检查组件的加载状态。
示例
顺序数据获取
顺序数据获取发生在树中的嵌套组件各自获取自己的数据且请求未被去重时,导致响应时间更长。

有时您可能需要这种模式,因为一个获取依赖于另一个的结果。
例如,<Playlists>
组件只有在 <Artist>
组件完成数据获取后才会开始获取数据,因为 <Playlists>
依赖于 artistID
prop:
为了改善用户体验,您应该使用 React <Suspense>
在数据获取时显示 fallback
。这将启用流式传输并防止整个路由被顺序数据请求阻塞。
并行数据获取
当路由中的数据请求被主动发起并同时开始时,就会发生并行数据获取。
默认情况下,布局和页面 (layouts and pages) 是并行渲染的。因此每个路由段会尽可能早地开始获取数据。
然而,在_任意_组件中,如果多个 async
/await
请求是先后放置的,它们仍可能是顺序执行的。例如,getAlbums
会阻塞直到 getArtist
解析完成:
你可以通过在数据使用组件之外定义请求,并使用 Promise.all
一起解析它们来并行发起请求:
须知: 当使用
Promise.all
时,如果其中一个请求失败,整个操作都会失败。为了处理这种情况,你可以改用Promise.allSettled
方法。
预加载数据
你可以通过创建一个工具函数来预加载数据,并在阻塞请求之前主动调用它。<Item>
会根据 checkIsAvailable()
函数的结果有条件地渲染。
你可以在 checkIsAvailable()
之前调用 preload()
来主动发起 <Item/>
的数据依赖请求。当 <Item/>
渲染时,它的数据已经被获取。
此外,你可以使用 React 的 cache
函数 和 server-only
包 来创建一个可复用的工具函数。这种方法可以缓存数据获取函数并确保它只在服务器端执行。