Next.js 如何在服务器组件和客户端组件中获取数据

2025-03-21 09:59 更新

如何在服务器组件和客户端组件中获取数据

本页将指导你如何在服务器组件和客户端组件中获取数据,以及如何流式传输依赖数据的内容。

服务器组件

你可以在服务器组件中使用以下方法获取数据:

  1. fetch API
  2. ORM 或数据库

使用 fetch API

要使用 fetch API 获取数据,将组件转换为异步函数,并等待 fetch 调用。例如:

  1. export default async function Page() {
  2. const data = await fetch('https://api.vercel.app/blog')
  3. const posts = await data.json()
  4. return (
  5. <ul>
  6. {posts.map((post) => (
  7. <li key={post.id}>{post.title}</li>
  8. ))}
  9. </ul>
  10. )
  11. }

使用 ORM 或数据库

你也可以使用 ORM 或数据库获取数据,将组件转换为异步函数,并等待调用:

  1. import { db, posts } from '@/lib/db'
  2. export default async function Page() {
  3. const allPosts = await db.select().from(posts)
  4. return (
  5. <ul>
  6. {allPosts.map((post) => (
  7. <li key={post.id}>{post.title}</li>
  8. ))}
  9. </ul>
  10. )
  11. }

客户端组件

在客户端组件中获取数据有两种方法:

  1. React 的 use 钩子
  2. 社区库(如 SWR 或 React Query)

使用 use 钩子

你可以使用 React 的 use 钩子从服务器到客户端流式传输数据。首先在服务器组件中获取数据,并将承诺传递给客户端组件作为属性:

  1. import Posts from '@/app/ui/posts'
  2. import { Suspense } from 'react'
  3. export default function Page() {
  4. // 不要等待数据获取函数
  5. const posts = getPosts()
  6. return (
  7. <Suspense fallback={<div>加载中...</div>}>
  8. <Posts posts={posts} />
  9. </Suspense>
  10. )
  11. }

然后,在客户端组件中,使用 use 钩子读取承诺:

  1. 'use client'
  2. import { use } from 'react'
  3. export default function Posts({
  4. posts,
  5. }: {
  6. posts: Promise<{ id: string; title: string }[]>
  7. }) {
  8. const allPosts = use(posts)
  9. return (
  10. <ul>
  11. {allPosts.map((post) => (
  12. <li key={post.id}>{post.title}</li>
  13. ))}
  14. </ul>
  15. )
  16. }

在上面的例子中,你需要将 <Posts /> 组件包裹在 <Suspense> 边界中。这意味着在承诺解决之前会显示备用内容。有关流式传输的更多信息,请参阅相关文档。

你还可以使用 SWR 或 React Query 等社区库在客户端组件中获取数据。这些库有自己的缓存、流式传输和其他功能的语义。例如,使用 SWR:

  1. 'use client'
  2. import useSWR from 'swr'
  3. const fetcher = (url) => fetch(url).then((r) => r.json())
  4. export default function BlogPage() {
  5. const { data, error, isLoading } = useSWR(
  6. 'https://api.vercel.app/blog',
  7. fetcher
  8. )
  9. if (isLoading) return <div>加载中...</div>
  10. if (error) return <div>错误: {error.message}</div>
  11. return (
  12. <ul>
  13. {data.map((post: { id: string; title: string }) => (
  14. <li key={post.id}>{post.title}</li>
  15. ))}
  16. </ul>
  17. )
  18. }

流式传输

警告:下面的内容假设你的应用程序中启用了 dynamicIO 配置选项。该标志在 Next.js 15 canary 中引入。

在服务器组件中使用 async/await 时,Next.js 将选择动态渲染。这意味着数据将为每个用户请求在服务器上获取并渲染。如果有任何缓慢的数据请求,整个路由将被阻止渲染。

为了提高初始加载时间和用户体验,你可以使用流式传输将页面的 HTML 分成更小的块,并逐步从服务器发送到客户端。

Next.js中文教程-流式传输

实现流式传输有两种方法:

  1. 使用 loading.js 文件
  2. 使用 React<Suspense> 组件

使用 loading.js

你可以在页面所在的文件夹中创建一个 loading.js 文件,在获取数据时流式传输整个页面。例如,要流式传输 app/blog/page.js,将文件添加到 app/blog 文件夹中。

Next.js中文教程-使用loading.js

  1. export default function Loading() {
  2. // 在这里定义加载 UI
  3. return <div>加载中...</div>
  4. }

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

此方法适用于路由段(布局和页面),但对于更精细的流式处理,您可以使用 <Suspense>

使用 <Suspense>

<Suspense> 允许你更细致地控制页面的哪些部分要流式传输。例如,你可以立即显示 <Suspense> 边界之外的任何页面内容,并在边界内流式传输博客文章列表。

  1. import { Suspense } from 'react'
  2. import BlogList from '@/components/BlogList'
  3. import BlogListSkeleton from '@/components/BlogListSkeleton'
  4. export default function BlogPage() {
  5. return (
  6. <div>
  7. {/* 这部分内容将立即发送到客户端 */}
  8. <header>
  9. <h1>欢迎来到编程狮的博客</h1>
  10. <p>请阅读下面的最新教程。</p>
  11. </header>
  12. <main>
  13. {/* 任何被 <Suspense> 包裹的内容都将被流式传输 */}
  14. <Suspense fallback={<BlogListSkeleton />}>
  15. <BlogList />
  16. </Suspense>
  17. </main>
  18. </div>
  19. )
  20. }

创建有意义的加载状态

即时加载状态是在导航后立即向用户显示的备用 UI。为了提供最佳的用户体验,我们建议设计有意义的加载状态,以帮助用户了解应用正在响应。例如,你可以使用骨架屏和加载动画,或者显示未来屏幕的一小部分,如封面照片、标题等。

在开发过程中,你可以使用 React Devtools 预览和检查组件的加载状态。

API 参考

阅读 API 参考 ,了解有关本页中提到的功能的更多信息。

  • 获取 扩展 fetch 函数的 API 参考。

  • loading.js loading.js 文件的 API 参考。
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号