本頁將指導(dǎo)你如何在服務(wù)器組件和客戶端組件中獲取數(shù)據(jù),以及如何流式傳輸依賴數(shù)據(jù)的內(nèi)容。
你可以在服務(wù)器組件中使用以下方法獲取數(shù)據(jù):
fetch
APIfetch
API
要使用 fetch
API 獲取數(shù)據(jù),將組件轉(zhuǎn)換為異步函數(shù),并等待 fetch
調(diào)用。例如:
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 或數(shù)據(jù)庫獲取數(shù)據(jù),將組件轉(zhuǎn)換為異步函數(shù),并等待調(diào)用:
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>
)
}
在客戶端組件中獲取數(shù)據(jù)有兩種方法:
use
鉤子use
鉤子
你可以使用 React 的 use
鉤子從服務(wù)器到客戶端流式傳輸數(shù)據(jù)。首先在服務(wù)器組件中獲取數(shù)據(jù),并將承諾傳遞給客戶端組件作為屬性:
import Posts from '@/app/ui/posts'
import { Suspense } from 'react'
export default function Page() {
// 不要等待數(shù)據(jù)獲取函數(shù)
const posts = getPosts()
return (
<Suspense fallback={<div>加載中...</div>}>
<Posts posts={posts} />
</Suspense>
)
}
然后,在客戶端組件中,使用 use
鉤子讀取承諾:
'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>
邊界中。這意味著在承諾解決之前會顯示備用內(nèi)容。有關(guān)流式傳輸?shù)母嘈畔?,請參閱相關(guān)文檔。
你還可以使用 SWR 或 React Query 等社區(qū)庫在客戶端組件中獲取數(shù)據(jù)。這些庫有自己的緩存、流式傳輸和其他功能的語義。例如,使用 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>加載中...</div>
if (error) return <div>錯誤: {error.message}</div>
return (
<ul>
{data.map((post: { id: string; title: string }) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}
警告:下面的內(nèi)容假設(shè)你的應(yīng)用程序中啟用了
dynamicIO
配置選項。該標(biāo)志在 Next.js 15 canary 中引入。
在服務(wù)器組件中使用 async/await
時,Next.js 將選擇動態(tài)渲染。這意味著數(shù)據(jù)將為每個用戶請求在服務(wù)器上獲取并渲染。如果有任何緩慢的數(shù)據(jù)請求,整個路由將被阻止渲染。
為了提高初始加載時間和用戶體驗,你可以使用流式傳輸將頁面的 HTML 分成更小的塊,并逐步從服務(wù)器發(fā)送到客戶端。
實現(xiàn)流式傳輸有兩種方法:
loading.js
文件<Suspense>
組件loading.js
你可以在頁面所在的文件夾中創(chuàng)建一個 loading.js
文件,在獲取數(shù)據(jù)時流式傳輸整個頁面。例如,要流式傳輸 app/blog/page.js
,將文件添加到 app/blog
文件夾中。
export default function Loading() {
// 在這里定義加載 UI
return <div>加載中...</div>
}
在導(dǎo)航時,用戶會立即看到布局和加載狀態(tài),同時頁面正在渲染。渲染完成后,新內(nèi)容將自動替換。
此方法適用于路由段(布局和頁面),但對于更精細(xì)的流式處理,您可以使用 <Suspense>
<Suspense>
<Suspense>
允許你更細(xì)致地控制頁面的哪些部分要流式傳輸。例如,你可以立即顯示 <Suspense>
邊界之外的任何頁面內(nèi)容,并在邊界內(nèi)流式傳輸博客文章列表。
import { Suspense } from 'react'
import BlogList from '@/components/BlogList'
import BlogListSkeleton from '@/components/BlogListSkeleton'
export default function BlogPage() {
return (
<div>
{/* 這部分內(nèi)容將立即發(fā)送到客戶端 */}
<header>
<h1>歡迎來到編程獅的博客</h1>
<p>請閱讀下面的最新教程。</p>
</header>
<main>
{/* 任何被 <Suspense> 包裹的內(nèi)容都將被流式傳輸 */}
<Suspense fallback={<BlogListSkeleton />}>
<BlogList />
</Suspense>
</main>
</div>
)
}
即時加載狀態(tài)是在導(dǎo)航后立即向用戶顯示的備用 UI。為了提供最佳的用戶體驗,我們建議設(shè)計有意義的加載狀態(tài),以幫助用戶了解應(yīng)用正在響應(yīng)。例如,你可以使用骨架屏和加載動畫,或者顯示未來屏幕的一小部分,如封面照片、標(biāo)題等。
在開發(fā)過程中,你可以使用 React Devtools 預(yù)覽和檢查組件的加載狀態(tài)。
閱讀 API 參考 ,了解有關(guān)本頁中提到的功能的更多信息。
更多建議: