React 的並行能力不再隻是框架內部的實作細節,而是複雜互動體驗的核心工具。搜尋、篩選、拖曳、富表單和資料面板都可能在同一時間觸發大量更新,團隊需要明確哪些更新必須立即完成,哪些更新可以延後。2026 年,並行模式已經從「可選的實驗特性」變成了 React 應用的預設行為。
渲染優先級要服務使用者意圖
並行 UI 的關鍵不是讓所有渲染都更快,而是讓使用者感知到重要操作更穩定。React 的排程模型將更新按優先級分為幾類:
緊急更新(Urgent Updates) 需要立即響應使用者操作的更新。典型場景包括:
- 輸入框的 typing 回饋
- 按鈕的 click 狀態變化
- 焦點切換和鍵盤導航
- 拖曳操作的即時位置更新
這些更新應該使用預設的同步渲染,不要包在 useTransition 或 useDeferredValue 裡。
過渡更新(Transition Updates) 可以延遲但需要保持一致的更新。React 18 引入的 useTransition 和 startTransition 是這類更新的核心工具:
- 搜尋結果列表的渲染
- 篩選條件變化後的列表重排
- Tab 切換後的內容區更新
- 圖表和圖表的重新計算
把重渲染標記為 transition 後,React 會在緊急更新處理完之後再執行它們,並且如果期間有新的使用者操作,會自動中斷當前的 transition。
延遲更新(Deferred Updates) 可以進一步延遲、甚至在下一幀才需要的更新。useDeferredValue 適用於:
- 大量資料的列表渲染(虛擬列表之外的場景)
- 非關鍵的資料面板刷新
- 後臺統計資料的更新
一個實用的規則:如果你不確定一個更新是否應該延遲,先把它包在 startTransition 裡,然後在 React DevTools Profiler 中觀察它是否阻塞了使用者互動。
Suspense 邊界的藝術
Suspense 可以讓載入狀態更可控,但邊界的選擇直接影響使用者體驗。
邊界不能太碎: 如果每個小元件都是一個獨立的 Suspense 邊界,頁面會出現大量局部載入動畫(spinner),使用者在等待時看到的是閃爍的碎片而不是一個整體。這比一次性載入完整體頁面更讓人不適。
邊界不能太粗糙: 如果整個頁面隻有一個 Suspense 邊界,那慢的元件會阻塞快的元件,一個資料慢會導致整個頁面都在 loading。
推薦的邊界策略:按使用者任務劃分
- 頁面骨架層:最外層 Suspense,包裹整個路由頁面,用於整體載入。fallback 顯示頁面骨架屏。
- 功能區域層:為獨立的使用者任務區域設定 Suspense。比如一個 Dashboard 頁面上,「篩選面板」、「資料表格」、「圖表區」、「推薦側欄」各自是獨立的 Suspense 邊界——使用者可以先生成篩選條件,再等資料載入。
- 元件級:隻在確有必要時才使用。一個參考標準:當某個元件的載入時間超過 2 秒且使用者可能在它載入期間操作其他區域時,才值得為它單獨設定 Suspense 邊界。
一個容易忽視的細節:Suspense 的 fallback 設計。好的 fallback 應該是當前區域的結構佔位,而不是一個轉圈的 spinner。用骨架屏代替 spinner,可以大幅減少使用者的等待焦慮。
useTransition 和 useDeferredValue 的選擇
這兩個 Hook 看起來相似,但適用場景不同:
使用 useTransition 當更新由使用者操作觸發:
function SearchPage() {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const handleInput = (e) => {
// 高優先級:立即更新輸入框的值
setQuery(e.target.value);
// 低優先級:搜尋結果可以等
startTransition(() => {
setSearchResults(searchData(e.target.value));
});
};
return (
<>
<input value={query} onChange={handleInput} />
{isPending && <SmallSpinner />}
<SearchResults results={searchResults} />
</>
);
}
使用 useDeferredValue 當更新來自外部資料來源:
function Dashboard({ serverData }) {
// 當 serverData 變化時,可以延遲舊頁面的渲染
const deferredData = useDeferredValue(serverData);
return (
<div style={{ opacity: serverData !== deferredData ? 0.5 : 1 }}>
<HeavyChart data={deferredData} />
</div>
);
}
關鍵區別:useTransition 給你 isPending 標誌(知道什麼時候在等待),useDeferredValue 給你新值和舊值的對比(可以同時展示舊資料並把新資料標記為 stale)。
並行模式下的效能診斷
React 2026 的效能診斷已經從「看渲染次數」升級到了「看互動鏈路」。三個工具各司其職:
React DevTools Profiler:
- 看元件為什麼渲染(props 變化、state 變化、context 變化、hooks 變化)
- 看每個 commit 的耗時
- 識別不必要的重新渲染
Chrome Performance 面板:
- 看主執行緒的長任務分佈
- 看 React 排程器的工作模式(是否正確地使用時間切片)
- 看互動事件的響應延遲(從使用者點選到瀏覽器處理)
RUM 資料(Web Vitals):
- INP(Interaction to Next Paint):2026 年最重要的互動指標,替代了 FID
- 按頁面、裝置和地區分組的 P75 和 P95 資料
- 發佈前後的對比資料
這三類資料要放在一起看:Profiler 告訴你「這個元件渲染了 5 次」,Performance 面板告訴你「這 5 次渲染佔用了 200ms 主執行緒」,RUM 告訴你「這個頁面的 P95 INP 是 180ms」。三者結合,才能判斷是否值得優化。
常見反模式
反模式 1:把一切包在 transition 裡 不是所有非緊急更新都需要 transition。如果一個更新的計算量很小(例如切換一個布林值),把它放進 transition 反而增加了複雜度。
反模式 2:在 transition 裡做副作用startTransition 隻應該包含狀態更新。如果在裡面發了網路請求、寫了 localStorage、觸發了埋點,React 中斷 transition 時狀態回滾了但副作用已經執行了——這會製造詭異的 bug。
反模式 3:用 useMemo/useCallback 代替並行特性 並行模式解決的是「渲染什麼時候發生」的問題,memoization 解決的是「渲染什麼內容」的問題。兩者互補,不能互相替代。
小結
React 2026 的並行實踐,本質是在複雜介面中重新分配計算資源。把高優先級互動(使用者正在操作的內容)保護好,把低優先級渲染(使用者可以等待的內容)安排在瀏覽器空閒時間執行。關鍵在於理解三個工具的分工:useTransition 用於使用者觸發的延遲更新,useDeferredValue 用於外部資料驅動的延遲渲染,Suspense 用於按使用者任務劃分載入邊界。做好這三件事,就能讓複雜的 React 應用在互動體驗上脫胎換骨。
