如何获取数据和流
本页将引导你了解如何在 服务器组件 和 客户端组件 中获取数据。以及如何依赖于数据的 stream 内容。
¥This page will walk you through how you can fetch data in Server Components and Client Components. As well as how to to stream content that depends on data.
获取数据
¥Fetching data
服务器组件
¥Server Components
你可以使用以下方式在服务器组件中获取数据:
¥You can fetch data in Server Components using:
使用 fetch
API
¥With the fetch
API
要使用 fetch
API 获取数据,请将你的组件变成异步函数,然后等待 fetch
调用。例如:
¥To fetch data with the fetch
API, turn your component into an asynchronous function, and await the fetch
call. For example:
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts = await data.json()
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
使用 ORM 或数据库
¥With an ORM or database
你可以通过将组件转换为异步函数并等待调用来使用 ORM 或数据库获取数据:
¥You can fetch data with an ORM or database by turning your component into an asynchronous function, and awaiting the call:
import { db, posts } from '@/lib/db'
export default async function Page() {
const allPosts = await db.select().from(posts)
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
import { db, posts } from '@/lib/db'
export default async function Page() {
const allPosts = await db.select().from(posts)
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
客户端组件
¥Client Components
有两种方法可以在客户端组件中获取数据,使用:
¥There are two ways to fetch data in Client Components, using:
-
React 的
use
钩¥React's
use
hook -
¥A community library like SWR or React Query
使用 use
钩子
¥With the use
hook
你可以使用 React 的 use
钩 到 stream 数据从服务器到客户端。首先从服务器组件中获取数据,然后将promise作为 prop 传递给客户端组件:
¥You can use React's use
hook to stream data from the server to client. Start by fetching data in your Server component, and pass the promise to your Client Component as prop:
import Posts from '@/app/ui/posts
import { Suspense } from 'react'
export default function Page() {
// Don't await the data fetching function
const posts = getPosts()
return (
<Suspense fallback={<div>Loading...</div>}>
<Posts posts={posts} />
</Suspense>
)
}
import Posts from '@/app/ui/posts
import { Suspense } from 'react'
export default function Page() {
// Don't await the data fetching function
const posts = getPosts()
return (
<Suspense fallback={<div>Loading...</div>}>
<Posts posts={posts} />
</Suspense>
)
}
然后,在你的客户端组件中,使用 use
钩子读取promise:
¥Then, in your Client Component, use the use
hook read the promise:
'use client'
import { use } from 'react'
export default function Posts({ posts }) {
const posts = use(posts)
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
'use client'
import { use } from 'react'
export default function Posts({ posts }) {
const posts = use(posts)
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
在上面的示例中,你需要将 <Posts />
组件封装在 <Suspense>
边界 中。这意味着在解决promise时将显示回退。了解有关 streaming 的更多信息。
¥In the example above, you need to wrap the <Posts />
component in a <Suspense>
boundary. This means the fallback will be shown while the promise is being resolved. Learn more about streaming.
社区库
¥Community libraries
你可以使用社区库(如 SWR 或 React 查询)在客户端组件中获取数据。这些库具有自己的缓存、流式传输和其他功能的语义。例如,使用 SWR:
¥You can use a community library like SWR or React Query to fetch data in Client Components. These libraries have their own semantics for caching, streaming, and other features. For example, with SWR:
'use client'
import useSWR from 'swr'
const fetcher = (url) => fetch(url).then((r) => r.json())
export default function BlogPage() {
const { data, error, isLoading } = useSWR(
'https://api.vercel.app/blog',
fetcher
)
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<ul>
{data.map((post: { id: string; title: string }) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
'use client'
import useSWR from 'swr'
const fetcher = (url) => fetch(url).then((r) => r.json())
export default function BlogPage() {
const { data, error, isLoading } = useSWR(
'https://api.vercel.app/blog',
fetcher
)
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<ul>
{data.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
流式
¥Streaming
警告:以下内容假定你的应用中已启用
dynamicIO
配置选项。该标志是在 Next.js 15 canary 中引入的。¥Warning: The content below assumes the
dynamicIO
config option is enabled in your application. The flag was introduced in Next.js 15 canary.
在服务器组件中使用 async/await
时,Next.js 将选择动态渲染。这意味着将为每个用户请求获取数据并在服务器上渲染。如果有任何缓慢的数据请求,整个路由将被阻止渲染。
¥When using async/await
in Server Components, Next.js will opt into dynamic rendering. This means the data will be fetched and rendered on the server for every user request. If there are any slow data requests, the whole route will be blocked from rendering.
为了改善初始加载时间和用户体验,你可以使用流式传输将页面的 HTML 分解为更小的块,并逐步将这些块从服务器发送到客户端。
¥To improve the initial load time and user experience, you can use streaming to break up the page's HTML into smaller chunks and progressively send those chunks from the server to the client.
有两种方法可以在应用中实现流式传输:
¥There are two ways you can implement streaming in your application:
-
¥With the
loading.js
file -
使用 React 的
<Suspense>
组件¥With React's
<Suspense>
component
使用 loading.js
¥With loading.js
你可以在与页面相同的文件夹中创建一个 loading.js
文件,以便在获取数据时流式传输整个页面。例如,要流式传输 app/blog/page.js
,请在 app/blog
文件夹中添加文件。
¥You can create a loading.js
file in the same folder as your page to stream the entire page while the data is being fetched. For example, to stream app/blog/page.js
, add the file inside the app/blog
folder.
export default function Loading() {
// Define the Loading UI here
return <div>Loading...</div>
}
export default function Loading() {
// Define the Loading UI here
return <div>Loading...</div>
}
在导航时,用户将在页面渲染时立即看到布局和 加载状态。渲染完成后,新内容将自动交换。
¥On navigation, the user will immediately see the layout and a loading state while the page is being rendered. The new content will then be automatically swapped in once rendering is complete.
在幕后,loading.js
将嵌套在 layout.js
内,并会自动将 page.js
文件及其下方的任何子文件封装在 <Suspense>
边界内。
¥Behind-the-scenes, loading.js
will be nested inside layout.js
, and will automatically wrap the page.js
file and any children below in a <Suspense>
boundary.
这种方法适用于路由段(布局和页面),但对于更细粒度的流式传输,你可以使用 <Suspense>
。
¥This approach works well for route segments (layouts and pages), but for more granular streaming, you can use <Suspense>
.
使用 <Suspense>
¥With <Suspense>
<Suspense>
允许你更详细地了解要流式传输的页面部分。例如,你可以立即显示任何超出 <Suspense>
边界的页面内容,并在边界内的博客文章列表中流式传输。
¥<Suspense>
allows you to be more granular about what parts of the page to stream. For example, you can immediately show any page content that falls outside of the <Suspense>
boundary, and stream in the list of blog posts inside the boundary.
import { Suspense } from 'react'
import BlogList from '@/components/BlogList'
import BlogListSkeleton from '@/components/BlogListSkeleton'
export default function BlogPage() {
return (
<div>
<header>
<h1>Welcome to the Blog</h1>
<p>Read the latest posts below.</p>
</header>
<main>
<Suspense fallback={<BlogListSkeleton />}>
<BlogList />
</Suspense>
</main>
</div>
)
}
import { Suspense } from 'react'
import BlogList from '@/components/BlogList'
import BlogListSkeleton from '@/components/BlogListSkeleton'
export default function BlogPage() {
return (
<div>
<header>
<h1>Welcome to the Blog</h1>
<p>Read the latest posts below.</p>
</header>
<main>
<Suspense fallback={<BlogListSkeleton />}>
<BlogList />
</Suspense>
</main>
</div>
)
}
创建有意义的加载状态
¥Creating meaningful loading states
即时加载状态是导航后立即向用户显示的后备 UI。为了获得最佳用户体验,我们建议设计有意义的加载状态,帮助用户了解应用正在响应。例如,你可以使用骨架和旋转器,或者未来屏幕中很小但有意义的部分,例如封面照片、标题等。
¥An instant loading state is fallback UI that is shown immediately to the user after navigation. For the best user experience, we recommend designing loading states that are meaningful and help users understand the app is responding. For example, you can use skeletons and spinners, or a small but meaningful part of future screens such as a cover photo, title, etc.
在开发过程中,你可以使用 React Devtools 预览和检查组件的加载状态。
¥In development, you can preview and inspect the loading state of your components using the React Devtools.