如何获取数据和流式处理
本页面将引导你了解如何在服务器组件和客户端组件中获取数据。以及如何流式处理依赖于数据的内容。
数据获取
服务器组件
你可以在服务器组件中使用以下方式获取数据
使用 fetch
API
要使用 fetch
API 获取数据,请将你的组件转换为异步函数,并等待 fetch
调用。例如
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 或数据库
由于服务器组件是在服务器上渲染的,你可以安全地使用 ORM 或数据库客户端进行数据库查询。将你的组件转换为异步函数,并等待调用
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>
)
}
客户端组件
在客户端组件中,有两种方法可以获取数据,使用
- React 的
use
Hook - 社区库,如 SWR 或 React Query
使用 use
Hook
你可以使用 React 的 use
Hook 来流式处理从服务器到客户端的数据。首先在你的服务器组件中获取数据,并将 Promise 作为 Props 传递给你的客户端组件
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
Hook 读取 Promise
'use client'
import { use } from 'react'
export default function Posts({
posts,
}: {
posts: Promise<{ id: string; title: string }[]>
}) {
const allPosts = use(posts)
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
在上面的示例中,你需要将 <Posts />
组件包裹在 <Suspense>
边界 中。这意味着在 Promise 被解析时,将显示 Fallback 内容。了解更多关于流式处理的信息。
社区库
你可以使用社区库,如 SWR 或 React Query 在客户端组件中获取数据。这些库有自己用于缓存、流式处理和其他功能的语义。例如,使用 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>
)
}
流式处理
警告: 以下内容假设你的应用程序中启用了
dynamicIO
配置选项。该标志在 Next.js 15 canary 版本中引入。
当在服务器组件中使用 async/await
时,Next.js 将选择动态渲染。这意味着数据将在服务器上为每个用户请求获取和渲染。如果存在任何缓慢的数据请求,整个路由的渲染将被阻止。
为了提高初始加载时间和用户体验,你可以使用流式处理将页面的 HTML 分解成更小的块,并将这些块从服务器逐步发送到客户端。

有两种方法可以在你的应用程序中实现流式处理
- 使用
loading.js
文件 - 使用 React 的
<Suspense>
组件
使用 loading.js
你可以在与你的页面相同的文件夹中创建一个 loading.js
文件,以便在获取数据时流式处理整个页面。例如,要流式处理 app/blog/page.js
,请将文件添加到 app/blog
文件夹中。

export default function Loading() {
// Define the Loading UI here
return <div>Loading...</div>
}
在导航时,用户将立即看到布局和一个加载状态,同时页面正在渲染。一旦渲染完成,新内容将自动替换进来。

在幕后,loading.js
将嵌套在 layout.js
内部,并将自动将 page.js
文件和任何子级包裹在 <Suspense>
边界中。

此方法适用于路由段(布局和页面),但对于更精细的流式处理,你可以使用 <Suspense>
。
使用 <Suspense>
<Suspense>
允许你更精细地控制要流式处理页面的哪些部分。例如,你可以立即显示 <Suspense>
边界之外的任何页面内容,并在边界内流式处理博客文章列表。
import { Suspense } from 'react'
import BlogList from '@/components/BlogList'
import BlogListSkeleton from '@/components/BlogListSkeleton'
export default function BlogPage() {
return (
<div>
{/* This content will be sent to the client immediately */}
<header>
<h1>Welcome to the Blog</h1>
<p>Read the latest posts below.</p>
</header>
<main>
{/* Any content wrapped in a <Suspense> boundary will be streamed */}
<Suspense fallback={<BlogListSkeleton />}>
<BlogList />
</Suspense>
</main>
</div>
)
}
创建有意义的加载状态
即时加载状态是在导航后立即向用户显示的 Fallback UI。为了获得最佳用户体验,我们建议设计有意义的加载状态,帮助用户理解应用程序正在响应。例如,你可以使用骨架屏和加载指示器,或者未来屏幕的一小部分但有意义的部分,例如封面照片、标题等。
在开发中,你可以使用 React Devtools 预览和检查组件的加载状态。
这有帮助吗?