1
1
---
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
4
4
title : 如何获取数据并实现流式传输
5
5
nav_title : 数据获取
6
6
description : 开始在您的应用中获取数据并流式传输内容。
@@ -14,20 +14,20 @@ related:
14
14
- app/api-reference/config/next-config-js/taint
15
15
---
16
16
17
- 本页将引导您了解如何在 [ 服务端与客户端组件] ( /docs/app/getting-started/server-and-client-components ) 中获取数据,以及如何流式传输依赖数据的组件 。
17
+ 本页将引导您了解如何在 [ 服务端与客户端组件 (Server and Client Components) ] ( /docs/app/getting-started/server-and-client-components ) 中获取数据,以及如何对依赖数据的组件进行 [ 流式传输 (streaming) ] ( #streaming ) 。
18
18
19
19
## 数据获取
20
20
21
21
### 服务端组件
22
22
23
- 您可以在服务端组件中通过以下方式获取数据 :
23
+ 您可以通过以下方式在服务端组件中获取数据 :
24
24
25
25
1 . 使用 [ ` fetch ` API] ( #with-the-fetch-api )
26
26
2 . 使用 [ ORM 或数据库] ( #with-an-orm-or-database )
27
27
28
28
#### 使用 ` fetch ` API
29
29
30
- 要通过 ` fetch ` API 获取数据,将您的组件转换为异步函数,并 await ` fetch ` 调用 。例如:
30
+ 要通过 ` fetch ` API 获取数据,请将组件转换为异步函数,并等待 ` fetch ` 调用完成 。例如:
31
31
32
32
``` tsx filename="app/blog/page.tsx" switcher
33
33
export default async function Page() {
@@ -59,12 +59,12 @@ export default async function Page() {
59
59
60
60
> ** 须知:**
61
61
>
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 ) 。
64
64
65
65
#### 使用 ORM 或数据库
66
66
67
- 由于服务端组件在服务器端渲染,您可以安全地使用 ORM 或数据库客户端进行查询。将组件转换为异步函数,并 await 调用 :
67
+ 由于服务端组件在服务器端渲染,您可以安全地使用 ORM 或数据库客户端进行查询。将组件转换为异步函数并等待调用完成 :
68
68
69
69
``` tsx filename="app/blog/page.tsx" switcher
70
70
import { db , posts } from ' @/lib/db'
@@ -98,21 +98,21 @@ export default async function Page() {
98
98
99
99
### 客户端组件
100
100
101
- 在客户端组件中有两种获取数据的方式 :
101
+ 在客户端组件中获取数据有两种方式 :
102
102
103
- 1 . 使用 React 的 [ ` use ` 钩子 ] ( https://react.dev/reference/react/use )
103
+ 1 . 使用 React 的 [ ` use ` hook ] ( https://react.dev/reference/react/use )
104
104
2 . 使用社区库如 [ SWR] ( https://swr.vercel.app/ ) 或 [ React Query] ( https://tanstack.com/query/latest )
105
105
106
- #### 使用 ` use ` 钩子流式传输数据
106
+ #### 使用 ` use ` hook 流式传输数据
107
107
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 传递给客户端组件:
109
109
110
110
``` tsx filename="app/blog/page.tsx" switcher
111
111
import Posts from ' @/app/ui/posts
112
112
import { Suspense } from ' react'
113
113
114
114
export default function Page() {
115
- // 不要 await 数据获取函数
115
+ // 不要等待数据获取函数
116
116
const posts = getPosts ()
117
117
118
118
return (
@@ -128,7 +128,7 @@ import Posts from '@/app/ui/posts
128
128
import { Suspense } from ' react'
129
129
130
130
export default function Page() {
131
- // 不要 await 数据获取函数
131
+ // 不要等待数据获取函数
132
132
const posts = getPosts()
133
133
134
134
return (
@@ -139,7 +139,7 @@ export default function Page() {
139
139
}
140
140
```
141
141
142
- 然后在客户端组件中使用 `use` 钩子读取 Promise:
142
+ 然后在客户端组件中使用 `use` hook 读取 Promise:
143
143
144
144
```tsx filename="app/ui/posts.tsx" switcher
145
145
' use client'
@@ -179,7 +179,7 @@ export default function Posts({ posts }) {
179
179
}
180
180
```
181
181
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) 的信息 。
183
183
184
184
#### 社区库
185
185
@@ -240,12 +240,12 @@ export default function BlogPage() {
240
240
241
241
> ** 警告:** 以下内容假设您的应用已启用 [ ` dynamicIO ` 配置选项] ( /docs/app/api-reference/config/next-config-js/dynamicIO ) 。该标志在 Next.js 15 canary 版本中引入。
242
242
243
- 在服务端组件中使用 ` async/await ` 时,Next.js 会启用 ** 动态渲染** 。这意味着数据将在每次用户请求时在服务器端获取并渲染 。如果有任何慢速数据请求,整个路由的渲染都会被阻塞 。
243
+ 在服务端组件中使用 ` async/await ` 时,Next.js 会启用 [ 动态渲染 (dynamic rendering) ] ( /docs/app/getting-started/partial-prerendering#dynamic-rendering ) 。这意味着数据将在服务器端为每个用户请求获取并渲染 。如果有任何慢速数据请求,整个路由的渲染将被阻塞 。
244
244
245
- 为了改善初始加载时间和用户体验,您可以使用流式传输将页面的 HTML 拆分为小块,并逐步从服务器发送到客户端 。
245
+ 为了改善初始加载时间和用户体验,您可以使用流式传输将页面的 HTML 拆分为较小的块,并逐步将这些块从服务器发送到客户端 。
246
246
247
247
<Image
248
- alt = " 服务端渲染与流式传输工作原理 "
248
+ alt = " 服务端渲染与流式传输的工作原理 "
249
249
srcLight = " /docs/light/server-rendering-with-streaming.png"
250
250
srcDark = " /docs/dark/server-rendering-with-streaming.png"
251
251
width = " 1600"
@@ -254,12 +254,12 @@ export default function BlogPage() {
254
254
255
255
有两种方式可以在应用中实现流式传输:
256
256
257
- 1 . 使用 [ ` loading.js ` 文件] ( #with-loadingjs )
258
- 2 . 使用 React 的 [ ` <Suspense> ` 组件 ] ( #with-suspense )
257
+ 1 . 使用 [ ` loading.js ` 文件] ( #with-loadingjs ) 包裹页面
258
+ 2 . 使用 [ ` <Suspense> ` ] ( #with-suspense ) 包裹组件
259
259
260
260
### 使用 ` loading.js `
261
261
262
- 您可以在页面所在文件夹中创建 ` loading.js ` 文件,在数据获取期间流式传输 ** 整个页面** 。例如要为 ` app/blog/page.js ` 添加流式传输 ,请在 ` app/blog ` 文件夹中添加该文件。
262
+ 您可以在页面所在文件夹中创建 ` loading.js ` 文件,在数据获取期间流式传输 ** 整个页面** 。例如,要流式传输 ` app/blog/page.js ` ,请在 ` app/blog ` 文件夹中添加该文件。
263
263
264
264
<Image
265
265
alt = " 包含 loading.js 文件的博客文件夹结构"
@@ -271,22 +271,22 @@ export default function BlogPage() {
271
271
272
272
``` tsx filename="app/blog/loading.tsx" switcher
273
273
export default function Loading() {
274
- // 在此定义加载 UI
274
+ // 在此定义加载状态 UI
275
275
return <div >Loading...</div >
276
276
}
277
277
```
278
278
279
279
``` jsx filename="app/blog/loading.js" switcher
280
280
export default function Loading () {
281
- // 在此定义加载 UI
281
+ // 在此定义加载状态 UI
282
282
return < div> Loading... < / div>
283
283
}
284
284
```
285
285
286
286
导航时,用户会立即看到布局和 [ 加载状态] ( #creating-meaningful-loading-states ) ,同时页面正在渲染。渲染完成后,新内容会自动替换显示。
287
287
288
288
<Image
289
- alt = " 加载 UI"
289
+ alt = " 加载状态 UI"
290
290
srcLight = " /docs/light/loading-ui.png"
291
291
srcDark = " /docs/dark/loading-ui.png"
292
292
width = " 1600"
@@ -323,7 +323,7 @@ export default function BlogPage() {
323
323
<p >阅读以下最新文章。</p >
324
324
</header >
325
325
<main >
326
- { /* 任何包裹在 <Suspense> 边界内的内容都会流式传输 */ }
326
+ { /* 任何包裹在 <Suspense> 边界内的内容都将被流式传输 */ }
327
327
<Suspense fallback = { <BlogListSkeleton />} >
328
328
<BlogList />
329
329
</Suspense >
@@ -347,7 +347,7 @@ export default function BlogPage() {
347
347
< p> 阅读以下最新文章。< / p>
348
348
< / header>
349
349
< main>
350
- {/* 任何包裹在 <Suspense> 边界内的内容都会流式传输 */ }
350
+ {/* 任何包裹在 <Suspense> 边界内的内容都将被流式传输 */ }
351
351
< Suspense fallback= {< BlogListSkeleton / > }>
352
352
< BlogList / >
353
353
< / Suspense>
@@ -359,15 +359,15 @@ export default function BlogPage() {
359
359
360
360
### 创建有意义的加载状态
361
361
362
- 即时加载状态是导航后立即向用户显示的 fallback UI。为了最佳用户体验,我们建议设计有意义的加载状态,帮助用户理解应用正在响应。例如可以使用骨架屏和加载动画,或未来屏幕中的一小部分有意义内容如封面图片 、标题等。
362
+ 即时加载状态是导航后立即向用户显示的 fallback UI。为了最佳用户体验,我们建议设计能帮助用户理解应用正在响应的有意义加载状态。例如,可以使用骨架屏和旋转器,或未来屏幕的一小部分有意义内容如封面图片 、标题等。
363
363
364
364
开发时,您可以使用 [ React Devtools] ( https://react.dev/learn/react-developer-tools ) 预览和检查组件的加载状态。
365
365
366
366
## 示例
367
367
368
368
### 顺序数据获取
369
369
370
- 顺序数据获取发生在树形结构的嵌套组件各自获取数据且请求未被 [ 去重] ( /docs/app/deep-dive/caching#request-memoization ) 时,会导致响应时间变长 。
370
+ 当树中的嵌套组件各自获取数据且请求未被 [ 去重 (deduplicated) ] ( /docs/app/deep-dive/caching#request-memoization ) 时,会发生顺序数据获取,导致响应时间延长 。
371
371
372
372
<Image
373
373
alt = " 顺序与并行数据获取"
@@ -377,7 +377,7 @@ export default function BlogPage() {
377
377
height = " 525"
378
378
/>
379
379
380
- 有时您可能需要这种模式,因为某个获取依赖于另一个获取的结果 。
380
+ 某些情况下您可能需要这种模式,因为一次获取依赖于另一次的结果 。
381
381
382
382
例如,` <Playlists> ` 组件只有在 ` <Artist> ` 组件完成数据获取后才会开始获取数据,因为 ` <Playlists> ` 依赖 ` artistID ` prop:
383
383
@@ -449,29 +449,29 @@ async function Playlists({ artistID }) {
449
449
}
450
450
```
451
451
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 ) 并防止整个路由被顺序数据请求阻塞。
453
453
454
454
### 并行数据获取
455
455
456
456
当路由中的数据请求被主动发起并同时开始时,就会发生并行数据获取。
457
457
458
- 默认情况下,[ 布局和页面] ( /docs/app/getting-started/layouts-and-pages ) 是并行渲染的。因此每个路由段会尽可能早地开始获取数据 。
458
+ 默认情况下,[ 布局和页面] ( /docs/app/getting-started/layouts-and-pages ) 是并行渲染的。因此每个路由段都会尽可能早地开始获取数据 。
459
459
460
- 然而,在_任何_组件内部,如果将多个 ` async ` /` await ` 请求按顺序放置,它们仍然会串行执行 。例如,` getAlbums ` 会阻塞直到 ` getArtist ` 完成解析 :
460
+ 然而,在_任何_组件内部,如果将多个 ` async ` /` await ` 请求顺序排列,它们仍会按顺序执行 。例如,` getAlbums ` 将一直阻塞,直到 ` getArtist ` 解析完成 :
461
461
462
462
``` tsx filename="app/artist/[username]/page.tsx" switcher
463
463
import { getArtist , getAlbums } from ' @/app/lib/data'
464
464
465
465
export default async function Page({ params }) {
466
- // 这些请求将串行执行
466
+ // 这些请求将按顺序执行
467
467
const { username } = await params
468
468
const artist = await getArtist (username )
469
469
const albums = await getAlbums (username )
470
470
return <div >{ artist .name } </div >
471
471
}
472
472
```
473
473
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 ) 一起解析它们来并行发起请求 :
475
475
476
476
``` tsx filename="app/artist/[username]/page.tsx" highlight={3,8,23} switcher
477
477
import Albums from ' ./albums'
@@ -537,13 +537,13 @@ export default async function Page({ params }) {
537
537
}
538
538
```
539
539
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 ) 方法替代。
541
541
542
- ### 数据预加载
542
+ ### 预加载数据
543
543
544
- 你可以通过创建一个工具函数,在阻塞请求之前主动调用来预加载数据 。` <Item> ` 组件会根据 ` checkIsAvailable() ` 函数的返回值条件渲染 。
544
+ 你可以通过创建一个工具函数来预加载数据,并在阻塞请求之前主动调用它 。` <Item> ` 会根据 ` checkIsAvailable() ` 函数的结果条件性渲染 。
545
545
546
- 你可以在 ` checkIsAvailable() ` 之前调用 ` preload() ` 来主动初始化 ` <Item/> ` 的数据依赖 。当 ` <Item/> ` 渲染时,其数据已经获取完成。
546
+ 你可以在 ` checkIsAvailable() ` 之前调用 ` preload() ` 来主动发起 ` <Item/> ` 的数据依赖请求 。当 ` <Item/> ` 渲染时,其数据已经获取完成。
547
547
548
548
``` tsx filename="app/item/[id]/page.tsx" switcher
549
549
import { getItem } from ' @/lib/data'
@@ -563,7 +563,7 @@ export default async function Page({
563
563
}
564
564
565
565
export const preload = (id : string ) => {
566
- // void 会执行给定表达式并返回 undefined
566
+ // void 运算符会执行给定表达式并返回 undefined
567
567
// https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
568
568
void getItem (id )
569
569
}
@@ -587,7 +587,7 @@ export default async function Page({ params }) {
587
587
}
588
588
589
589
export const preload = (id ) => {
590
- // void 会执行给定表达式并返回 undefined
590
+ // void 运算符会执行给定表达式并返回 undefined
591
591
// https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/void
592
592
void getItem (id)
593
593
}
0 commit comments