ReactNext.js

React use() 與 Suspense:無瀑布的資料串流

React use() 與 Suspense:無瀑布的資料串流

瀑布有一個醜陋的算術特性。四個 300ms 請求串聯在一起,對於本可以在 300ms 到達的資料,產生了 1.2 秒的空白螢幕。

暫停的元件實際上做了什麼

未準備好的元件拋出一個 Promise。不是錯誤,是 Promise。React 像 Error Boundary 捕獲錯誤一樣捕獲它,丟棄部分渲染的工作,顯示最近的後備,並訂閱被拋出的 Promise。當它解析時,React 從頭再次執行元件。

Promise 在這些執行之間必須保持穩定。每次執行都是新的 Promise 意味著無限暫停。這是破壞 use() 最常見的方式。

tsx

"use client";
import { use } from "react";
export default function Posts({ posts }) {
// 暫停直到 promise 解析
const allPosts = use(posts);
return (
<ul>
{allPosts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}

Suspense、靜態外殼和錯誤順序到達的 HTML

React 立即發送靜態外殼。延遲的部分被添加到文件的末尾,不可見,React 在其旁邊串流一個小的內聯 <script>,將內容移動到正確的位置。

tsx

import { Suspense } from "react";
export default function Page() {
return (
<main>
<h1>儀表板</h1> {/* 立即發送 */}
<Suspense fallback={<StatsSkeleton />}>
<Stats />
</Suspense>
<Suspense fallback={<FeedSkeleton />}>
<Feed />
</Suspense>
</main>
);
}

早開始,晚讀取

tsx

import { Suspense } from "react";
import Posts from "@/app/ui/posts";
import Comments from "@/app/ui/comments";
export default function Page() {
// 兩個請求現在並行開始
const posts = getPosts();
const comments = getComments();
return (
<>
<Suspense fallback={<div>載入文章中...</div>}>
<Posts posts={posts} />
</Suspense>
<Suspense fallback={<div>載入評論中...</div>}>
<Comments comments={comments} />
</Suspense>
</>
);
}

沒有人預期的陷阱:串流使用你的 HTTP 狀態碼

當 Suspense 後備渲染並且串流打開時,伺服器已經提交了 200 OK。在串流邊界內部觸發的 notFound() 無法提供真正的 404。

tsx

export default async function PostPage({ params }) {
const { slug } = await params;
const exists = await checkSlugExists(slug); // 快速,在串流打開之前
if (!exists) notFound(); // 真正的 404
return (
<Suspense fallback={<PostSkeleton />}>
<PostContent slug={slug} />
</Suspense>
);
}

串流對 Core Web Vitals 的影響

  • TTFB 下降至大約佈局渲染時間。
  • LCP 可能惡化,而不是改善。 將 LCP 元素保留在靜態外殼中。
  • CLS 是你的責任。
  • INP 通過選擇性水化得到改善。

陷阱與最佳實踐

  • 永遠不要在 Client Component 渲染期間建立 Promise。
  • Suspense 捕獲等待,而不是錯誤。 將每個邊界與 Error Boundary 配對。
  • 像人感知的那樣調整邊界大小。
  • 機器人不接收串流。
  • Promise prop 跨越網路傳遞。

總結

不再是序列——請求、等待、渲染、請求、等待、渲染——你得到並行串流:所有事情同時開始,頁面的每個部分在其自己的資料到達後立即顯示。