11---
2- source-updated-at : 2025-05-21T18:33:43 .000Z
3- translation-updated-at : 2025-05-21T19:19:09.115Z
2+ source-updated-at : 2025-05-22T15:18:56 .000Z
3+ translation-updated-at : 2025-05-23T16:46:50.155Z
44title : 如何获取数据并实现流式传输
55nav_title : 数据获取
66description : 开始在您的应用中获取数据并流式传输内容。
@@ -14,20 +14,20 @@ related:
1414 - app/api-reference/config/next-config-js/taint
1515---
1616
17- 本页将引导您了解如何在 [ 服务端与客户端组件] ( /docs/app/getting-started/server-and-client-components ) 中获取数据,以及如何流式传输依赖数据的组件 。
17+ 本页将引导您了解如何在 [ 服务端与客户端组件 (Server and Client Components) ] ( /docs/app/getting-started/server-and-client-components ) 中获取数据,以及如何对依赖数据的组件进行 [ 流式传输 (streaming) ] ( #streaming ) 。
1818
1919## 数据获取
2020
2121### 服务端组件
2222
23- 您可以在服务端组件中通过以下方式获取数据 :
23+ 您可以通过以下方式在服务端组件中获取数据 :
2424
25251 . 使用 [ ` fetch ` API] ( #with-the-fetch-api )
26262 . 使用 [ ORM 或数据库] ( #with-an-orm-or-database )
2727
2828#### 使用 ` fetch ` API
2929
30- 要通过 ` fetch ` API 获取数据,将您的组件转换为异步函数,并 await ` fetch ` 调用 。例如:
30+ 要通过 ` fetch ` API 获取数据,请将组件转换为异步函数,并等待 ` fetch ` 调用完成 。例如:
3131
3232``` tsx filename="app/blog/page.tsx" switcher
3333export default async function Page() {
@@ -59,12 +59,12 @@ export default async function Page() {
5959
6060> ** 须知:**
6161>
62- > - ` fetch ` 响应默认不会被缓存。但 Next.js 会对路由进行 [ 预渲染] ( /docs/app/getting-started/partial-prerendering#static-rendering ) ,输出结果会被缓存以提升性能 。如需启用 [ 动态渲染] ( /docs/app/getting-started/partial-prerendering#dynamic-rendering ) ,请使用 ` { cache: 'no-store' } ` 选项。参阅 [ ` fetch ` API 参考] ( /docs/app/api-reference/functions/fetch ) 。
63- > - 开发环境下,您可以记录 ` fetch ` 调用以便更好地调试和观察。参阅 [ ` logging ` API 参考] ( /docs/app/api-reference/config/next-config-js/logging ) 。
62+ > - ` fetch ` 响应默认不会被缓存。但 Next.js 会 [ 预渲染 (prerender) ] ( /docs/app/getting-started/partial-prerendering#static-rendering ) 路由,其输出会被缓存以提高性能 。如需启用 [ 动态渲染 (dynamic rendering) ] ( /docs/app/getting-started/partial-prerendering#dynamic-rendering ) ,请使用 ` { cache: 'no-store' } ` 选项。详见 [ ` fetch ` API 参考] ( /docs/app/api-reference/functions/fetch ) 。
63+ > - 开发环境下,您可以记录 ` fetch ` 调用以便更好地调试和观察。参见 [ ` logging ` API 参考] ( /docs/app/api-reference/config/next-config-js/logging ) 。
6464
6565#### 使用 ORM 或数据库
6666
67- 由于服务端组件在服务器端渲染,您可以安全地使用 ORM 或数据库客户端进行查询。将组件转换为异步函数,并 await 调用 :
67+ 由于服务端组件在服务器端渲染,您可以安全地使用 ORM 或数据库客户端进行查询。将组件转换为异步函数并等待调用完成 :
6868
6969``` tsx filename="app/blog/page.tsx" switcher
7070import { db , posts } from ' @/lib/db'
@@ -98,21 +98,21 @@ export default async function Page() {
9898
9999### 客户端组件
100100
101- 在客户端组件中有两种获取数据的方式 :
101+ 在客户端组件中获取数据有两种方式 :
102102
103- 1 . 使用 React 的 [ ` use ` 钩子 ] ( https://react.dev/reference/react/use )
103+ 1 . 使用 React 的 [ ` use ` hook ] ( https://react.dev/reference/react/use )
1041042 . 使用社区库如 [ SWR] ( https://swr.vercel.app/ ) 或 [ React Query] ( https://tanstack.com/query/latest )
105105
106- #### 使用 ` use ` 钩子流式传输数据
106+ #### 使用 ` use ` hook 流式传输数据
107107
108- 您可以使用 React 的 [ ` use ` 钩子 ] ( https://react.dev/reference/react/use ) 将数据从服务器 [ 流式传输] ( #streaming ) 到客户端。首先在服务端组件中获取数据,然后将 Promise 作为 prop 传递给客户端组件:
108+ 您可以使用 React 的 [ ` use ` hook ] ( https://react.dev/reference/react/use ) 将数据从服务器 [ 流式传输 (streaming) ] ( #streaming ) 到客户端。首先在服务端组件中获取数据,然后将 Promise 作为 prop 传递给客户端组件:
109109
110110``` tsx filename="app/blog/page.tsx" switcher
111111import Posts from ' @/app/ui/posts
112112import { Suspense } from ' react'
113113
114114export default function Page() {
115- // 不要 await 数据获取函数
115+ // 不要等待数据获取函数
116116 const posts = getPosts ()
117117
118118 return (
@@ -128,7 +128,7 @@ import Posts from '@/app/ui/posts
128128import { Suspense } from ' react'
129129
130130export default function Page() {
131- // 不要 await 数据获取函数
131+ // 不要等待数据获取函数
132132 const posts = getPosts()
133133
134134 return (
@@ -139,7 +139,7 @@ export default function Page() {
139139}
140140```
141141
142- 然后在客户端组件中使用 `use` 钩子读取 Promise:
142+ 然后在客户端组件中使用 `use` hook 读取 Promise:
143143
144144```tsx filename="app/ui/posts.tsx" switcher
145145' use client'
@@ -179,7 +179,7 @@ export default function Posts({ posts }) {
179179}
180180```
181181
182- 在上例中,您需要用 [`<Suspense>` 边界](https://react.dev/reference/react/Suspense) 包裹 `<Posts />` 组件 。这意味着在 Promise 解析期间会显示 fallback UI 。了解更多关于 [流式传输](#streaming) 的内容 。
182+ 在上例中,`<Posts>` 组件被包裹在 [`<Suspense>` 边界](https://react.dev/reference/react/Suspense) 内 。这意味着在 Promise 解析期间会显示 fallback 内容 。了解更多关于 [流式传输 (streaming) ](#streaming) 的信息 。
183183
184184#### 社区库
185185
@@ -240,12 +240,12 @@ export default function BlogPage() {
240240
241241> ** 警告:** 以下内容假设您的应用已启用 [ ` dynamicIO ` 配置选项] ( /docs/app/api-reference/config/next-config-js/dynamicIO ) 。该标志在 Next.js 15 canary 版本中引入。
242242
243- 在服务端组件中使用 ` async/await ` 时,Next.js 会启用 ** 动态渲染** 。这意味着数据将在每次用户请求时在服务器端获取并渲染 。如果有任何慢速数据请求,整个路由的渲染都会被阻塞 。
243+ 在服务端组件中使用 ` async/await ` 时,Next.js 会启用 [ 动态渲染 (dynamic rendering) ] ( /docs/app/getting-started/partial-prerendering#dynamic-rendering ) 。这意味着数据将在服务器端为每个用户请求获取并渲染 。如果有任何慢速数据请求,整个路由的渲染将被阻塞 。
244244
245- 为了改善初始加载时间和用户体验,您可以使用流式传输将页面的 HTML 拆分为小块,并逐步从服务器发送到客户端 。
245+ 为了改善初始加载时间和用户体验,您可以使用流式传输将页面的 HTML 拆分为较小的块,并逐步将这些块从服务器发送到客户端 。
246246
247247<Image
248- alt = " 服务端渲染与流式传输工作原理 "
248+ alt = " 服务端渲染与流式传输的工作原理 "
249249 srcLight = " /docs/light/server-rendering-with-streaming.png"
250250 srcDark = " /docs/dark/server-rendering-with-streaming.png"
251251 width = " 1600"
@@ -254,12 +254,12 @@ export default function BlogPage() {
254254
255255有两种方式可以在应用中实现流式传输:
256256
257- 1 . 使用 [ ` loading.js ` 文件] ( #with-loadingjs )
258- 2 . 使用 React 的 [ ` <Suspense> ` 组件 ] ( #with-suspense )
257+ 1 . 使用 [ ` loading.js ` 文件] ( #with-loadingjs ) 包裹页面
258+ 2 . 使用 [ ` <Suspense> ` ] ( #with-suspense ) 包裹组件
259259
260260### 使用 ` loading.js `
261261
262- 您可以在页面所在文件夹中创建 ` loading.js ` 文件,在数据获取期间流式传输 ** 整个页面** 。例如要为 ` app/blog/page.js ` 添加流式传输 ,请在 ` app/blog ` 文件夹中添加该文件。
262+ 您可以在页面所在文件夹中创建 ` loading.js ` 文件,在数据获取期间流式传输 ** 整个页面** 。例如,要流式传输 ` app/blog/page.js ` ,请在 ` app/blog ` 文件夹中添加该文件。
263263
264264<Image
265265 alt = " 包含 loading.js 文件的博客文件夹结构"
@@ -271,22 +271,22 @@ export default function BlogPage() {
271271
272272``` tsx filename="app/blog/loading.tsx" switcher
273273export default function Loading() {
274- // 在此定义加载 UI
274+ // 在此定义加载状态 UI
275275 return <div >Loading...</div >
276276}
277277```
278278
279279``` jsx filename="app/blog/loading.js" switcher
280280export default function Loading () {
281- // 在此定义加载 UI
281+ // 在此定义加载状态 UI
282282 return < div> Loading... < / div>
283283}
284284```
285285
286286导航时,用户会立即看到布局和 [ 加载状态] ( #creating-meaningful-loading-states ) ,同时页面正在渲染。渲染完成后,新内容会自动替换显示。
287287
288288<Image
289- alt = " 加载 UI"
289+ alt = " 加载状态 UI"
290290 srcLight = " /docs/light/loading-ui.png"
291291 srcDark = " /docs/dark/loading-ui.png"
292292 width = " 1600"
@@ -323,7 +323,7 @@ export default function BlogPage() {
323323 <p >阅读以下最新文章。</p >
324324 </header >
325325 <main >
326- { /* 任何包裹在 <Suspense> 边界内的内容都会流式传输 */ }
326+ { /* 任何包裹在 <Suspense> 边界内的内容都将被流式传输 */ }
327327 <Suspense fallback = { <BlogListSkeleton />} >
328328 <BlogList />
329329 </Suspense >
@@ -347,7 +347,7 @@ export default function BlogPage() {
347347 < p> 阅读以下最新文章。< / p>
348348 < / header>
349349 < main>
350- {/* 任何包裹在 <Suspense> 边界内的内容都会流式传输 */ }
350+ {/* 任何包裹在 <Suspense> 边界内的内容都将被流式传输 */ }
351351 < Suspense fallback= {< BlogListSkeleton / > }>
352352 < BlogList / >
353353 < / Suspense>
@@ -359,15 +359,15 @@ export default function BlogPage() {
359359
360360### 创建有意义的加载状态
361361
362- 即时加载状态是导航后立即向用户显示的 fallback UI。为了最佳用户体验,我们建议设计有意义的加载状态,帮助用户理解应用正在响应。例如可以使用骨架屏和加载动画,或未来屏幕中的一小部分有意义内容如封面图片 、标题等。
362+ 即时加载状态是导航后立即向用户显示的 fallback UI。为了最佳用户体验,我们建议设计能帮助用户理解应用正在响应的有意义加载状态。例如,可以使用骨架屏和旋转器,或未来屏幕的一小部分有意义内容如封面图片 、标题等。
363363
364364开发时,您可以使用 [ React Devtools] ( https://react.dev/learn/react-developer-tools ) 预览和检查组件的加载状态。
365365
366366## 示例
367367
368368### 顺序数据获取
369369
370- 顺序数据获取发生在树形结构的嵌套组件各自获取数据且请求未被 [ 去重] ( /docs/app/deep-dive/caching#request-memoization ) 时,会导致响应时间变长 。
370+ 当树中的嵌套组件各自获取数据且请求未被 [ 去重 (deduplicated) ] ( /docs/app/deep-dive/caching#request-memoization ) 时,会发生顺序数据获取,导致响应时间延长 。
371371
372372<Image
373373 alt = " 顺序与并行数据获取"
@@ -377,7 +377,7 @@ export default function BlogPage() {
377377 height = " 525"
378378/>
379379
380- 有时您可能需要这种模式,因为某个获取依赖于另一个获取的结果 。
380+ 某些情况下您可能需要这种模式,因为一次获取依赖于另一次的结果 。
381381
382382例如,` <Playlists> ` 组件只有在 ` <Artist> ` 组件完成数据获取后才会开始获取数据,因为 ` <Playlists> ` 依赖 ` artistID ` prop:
383383
@@ -449,29 +449,29 @@ async function Playlists({ artistID }) {
449449}
450450```
451451
452- 为了改善用户体验,您应该使用 [ React ` <Suspense> ` ] ( /docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense ) 在数据获取时显示 ` fallback ` 。这将启用 [ 流式传输] ( #streaming ) 并防止整个路由被顺序数据请求阻塞。
452+ 为了改善用户体验,您应该使用 [ React ` <Suspense> ` ] ( /docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense ) 在数据获取期间显示 ` fallback ` 。这将启用 [ 流式传输 (streaming) ] ( #streaming ) 并防止整个路由被顺序数据请求阻塞。
453453
454454### 并行数据获取
455455
456456当路由中的数据请求被主动发起并同时开始时,就会发生并行数据获取。
457457
458- 默认情况下,[ 布局和页面] ( /docs/app/getting-started/layouts-and-pages ) 是并行渲染的。因此每个路由段会尽可能早地开始获取数据 。
458+ 默认情况下,[ 布局和页面] ( /docs/app/getting-started/layouts-and-pages ) 是并行渲染的。因此每个路由段都会尽可能早地开始获取数据 。
459459
460- 然而,在_任何_组件内部,如果将多个 ` async ` /` await ` 请求按顺序放置,它们仍然会串行执行 。例如,` getAlbums ` 会阻塞直到 ` getArtist ` 完成解析 :
460+ 然而,在_任何_组件内部,如果将多个 ` async ` /` await ` 请求顺序排列,它们仍会按顺序执行 。例如,` getAlbums ` 将一直阻塞,直到 ` getArtist ` 解析完成 :
461461
462462``` tsx filename="app/artist/[username]/page.tsx" switcher
463463import { getArtist , getAlbums } from ' @/app/lib/data'
464464
465465export default async function Page({ params }) {
466- // 这些请求将串行执行
466+ // 这些请求将按顺序执行
467467 const { username } = await params
468468 const artist = await getArtist (username )
469469 const albums = await getAlbums (username )
470470 return <div >{ artist .name } </div >
471471}
472472```
473473
474- 你可以通过在数据使用组件外部定义请求 ,并使用 [ ` Promise.all ` ] ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all ) 一起解析它们来实现并行请求 :
474+ 你可以通过在数据使用组件之外定义请求 ,并使用 [ ` Promise.all ` ] ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all ) 一起解析它们来并行发起请求 :
475475
476476``` tsx filename="app/artist/[username]/page.tsx" highlight={3,8,23} switcher
477477import Albums from ' ./albums'
@@ -537,13 +537,13 @@ export default async function Page({ params }) {
537537}
538538```
539539
540- > ** 须知:** 使用 ` Promise.all ` 时,如果其中一个请求失败,整个操作都会失败。要处理这种情况 ,可以使用 [ ` Promise.allSettled ` ] ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled ) 方法替代。
540+ > ** 须知:** 使用 ` Promise.all ` 时,如果其中一个请求失败,整个操作都会失败。为了处理这种情况 ,可以使用 [ ` Promise.allSettled ` ] ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled ) 方法替代。
541541
542- ### 数据预加载
542+ ### 预加载数据
543543
544- 你可以通过创建一个工具函数,在阻塞请求之前主动调用来预加载数据 。` <Item> ` 组件会根据 ` checkIsAvailable() ` 函数的返回值条件渲染 。
544+ 你可以通过创建一个工具函数来预加载数据,并在阻塞请求之前主动调用它 。` <Item> ` 会根据 ` checkIsAvailable() ` 函数的结果条件性渲染 。
545545
546- 你可以在 ` checkIsAvailable() ` 之前调用 ` preload() ` 来主动初始化 ` <Item/> ` 的数据依赖 。当 ` <Item/> ` 渲染时,其数据已经获取完成。
546+ 你可以在 ` checkIsAvailable() ` 之前调用 ` preload() ` 来主动发起 ` <Item/> ` 的数据依赖请求 。当 ` <Item/> ` 渲染时,其数据已经获取完成。
547547
548548``` tsx filename="app/item/[id]/page.tsx" switcher
549549import { getItem } from ' @/lib/data'
@@ -563,7 +563,7 @@ export default async function Page({
563563}
564564
565565export const preload = (id : string ) => {
566- // void 会执行给定表达式并返回 undefined
566+ // void 运算符会执行给定表达式并返回 undefined
567567 // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
568568 void getItem (id )
569569}
@@ -587,7 +587,7 @@ export default async function Page({ params }) {
587587}
588588
589589export const preload = (id ) => {
590- // void 会执行给定表达式并返回 undefined
590+ // void 运算符会执行给定表达式并返回 undefined
591591 // https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
592592 void getItem (id)
593593}
0 commit comments