餓死抬頭,React 19 究竟是繼 v18 睽違兩年華麗登場?! 還是十年走來始終如一?! 相對於 Angular 每次升級每次 Breaking Change,這次的 React 19 更新看起來不僅不想拖過年,也把 Nextjs Canary 許久的 Server Component 穩定版進行釋出。
一如既往的可以無痛升級,這次升級雖然也有一些必要改變,但也提早在 v18 的小版本釋出中提出警告,基於 v18 穩定和表現基礎上實現了多項重大功能提升和優化,接近兩年的蟄伏究竟帶來了什麼改變?!
React 19 不僅僅是版本更新,而是對開發者體驗和應用性能的全方位升級。React 19 引入了全新的 API 和 Hooks,改善了 React 內部多個機制,讓開發者能夠更高效地構建現代 Web 應用。
接下來一起來從實際開發者體驗的角度,介紹 React 19 中幾個關鍵新特性,並解釋它們如何簡化開發流程和提升應用效能。
React 19 Server Component
目前看起來雖然穩定,但絕大多數工具跟框架都尚未完全整合和支援,小編認為這一版可以看成 Server Component 提前準備,像是
- Actions 提供了 Client 跟 Server 都支援的寫法
- 內建支援使用 Meta Data
- 完整支援 web component
React 19 Actions
React 19 介紹了 Actions 的概念,取代了使用事件處理器,並與 React transitions 和 concurrent features 整合。
Actions 可以在客戶端和伺服器端使用,例如可以使用 Client Action 來替代之前的 onSubmit 事件處理器來處理表單,使用 Action 時不需要解析事件,Action 會直接接收 FormData。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import { useState } from "react";
export default function TodoApp() { const [items, setItems] = useState([{ text: "我的第一個待辦事項" }]);
async function formAction(formData) { const newItem = formData.get("item"); setItems((items) => [...items, { text: newItem }]); }
return ( <> <h1>待辦清單</h1> <form action={formAction}> <input type="text" name="item" placeholder="新增待辦..." /> <button type="submit">新增</button> </form> <ul> {items.map((item, index) => ( <li key={index}>{item.text}</li> ))} </ul> </> ); }
|
useActionState:非同步狀態管理
React 19 中加入了 useActionState Hook 簡化程式碼,專門用來簡化非同步資料處理和狀態管理,開發者不再需要手動追蹤請求狀態、錯誤信息或更新順序,Actions 提供自動管理的待處理狀態,當請求開始時 isPending
會設置為 true,並在最終更新後自動重置。
例如當用戶提交表單時,React 19 可以自動管理 API 請求的等待狀態和錯誤處理,這使得開發者能更專注於業務邏輯,而不必關心繁瑣的狀態管理細節。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| function UpdateName({}) { const [name, setName] = useState(""); const [error, setError] = useState(null); const [isPending, startTransition] = useTransition();
const handleSubmit = () => { startTransition(async () => { const error = await updateName(name); if (error) { setError(error); return; } redirect("/path"); }); };
return ( <div> <input value={name} onChange={(event) => setName(event.target.value)} /> <button onClick={handleSubmit} disabled={isPending}> 更新 </button> {error && <p>{error}</p>} </div> ); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function ChangeName({ name, setName }) { const [error, submitAction, isPending] = useActionState( async (previousState, formData) => { const error = await updateName(formData.get("name")); if (error) { return error; } redirect("/path"); return null; }, null );
return ( <form action={submitAction}> <input type="text" name="name" /> <button type="submit" disabled={isPending}> 更新 </button> {error && <p>{error}</p>} </form> ); }
|
useOptimistic:提升用戶體驗
React 19 引入了 useOptimistic Hook,通過實現樂觀更新來提升應用的響應速度。用戶送出請求後,應用會立刻顯示預期結果,並在後端回應後進行最終更新而減少等待時間。
- 如果成功,會直接用新的 state 覆蓋預期結果
- 如果失敗,會直接取消 UI 的更新
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function ChangeName({ currentName, onUpdateName }) { const [optimisticName, setOptimisticName] = useOptimistic(currentName);
const submitAction = async (formData) => { const newName = formData.get("name"); setOptimisticName(newName); const updatedName = await updateName(newName); onUpdateName(updatedName); };
return ( <form action={submitAction}> <p>Your name is: {optimisticName}</p> <p> <label>Change Name:</label> <input type="text" name="name" disabled={currentName !== optimisticName} /> </p> </form> ); }
|
use API:輕鬆讀取資源
React 19 引入的 use API 讓開發者能夠在渲染過程中直接讀取非同步資源(如 Promise 或 Context),並且由 React 自動處理懸掛(suspense)機制,直到資源準備好為止,這樣就可以簡化資料的處理流程,不必手動管理載入狀態。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { use } from "react";
function Comments({ commentsPromise }) { const comments = use(commentsPromise); return comments.map((comment) => <p key={comment.id}>{comment}</p>); }
function Page({ commentsPromise }) { return ( <Suspense fallback={<div>Loading...</div>}> <Comments commentsPromise={commentsPromise} /> </Suspense> ); }
|
簡化 ref 使用
React 19 改善了對 ref 的使用,支援將 ref 作為 props 傳遞給函式元件,並增加了對 ref 清理函式的支援。這使得管理元件的 ref 更加簡單且直觀,不再需要使用 forwardRef。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function MyInput({ placeholder, ref }) { return ( <input placeholder={placeholder} ref={(ref) => { // ref 被創建
// 新增:返回一個清理函式,當元素從 DOM 中移除時重置 ref return () => { // ref 清理邏輯 }; }} /> ); }
<MyInput ref={ref} />;
|
useDeferredValue:延遲更新優化性能
React 19 引入了 useDeferredValue,它允許在初始渲染時使用預設值,並將數據更新推遲至背景中進行,這樣就能有效減少數據變更對渲染性能的影響。
1 2 3 4 5 6 7
| function Search({ deferredValue }) { const value = useDeferredValue(deferredValue, "");
return <Results query={value} />; }
|
React 19 提升樣式和資源載入效能
React 19 針對樣式表和資源載入進行了優化,支援在客戶端渲染和伺服器端渲染中更好地整合和載入資源,從而提升應用的載入速度和流暢度。
開發者可以更加靈活地控制樣式表的載入順序,保證依賴樣式表的內容在樣式表載入後才會渲染,避免因樣式表未載入完全而導致的渲染問題。
在 React 19 中 對 Meta Data 提供了原生支援,實現了在元件中渲染文檔標籤(如 <title>
、<link>
和 <meta>
),並將它們自動提升到文檔的 <head>
區域。
這確保了這些元資料標籤在客戶端應用、流式 SSR 和伺服器端元件中的正常運作,從而提升了性能,雖然 React 提供了內建支援,對於更複雜的場景,您仍然可以選擇使用第三方庫來處理元資料,例如 react-helmet,它可以根據當前路由動態更新元資料。
1 2 3 4 5 6 7 8 9 10 11 12
| function BlogPost({ post }) { return ( <article> <h1>{post.title}</h1> <title>{post.title}</title> <meta name="author" content="Josh" /> <link rel="author" href="https://twitter.com/joshcstory/" /> <meta name="keywords" content={post.keywords} /> <p>Eee equals em-see-squared...</p> </article> ); }
|
優化樣式表載入
React 19 引入了樣式表的內建支援,進一步提升了客戶端和伺服器端渲染的性能。
在渲染包含樣式表的元件時,React 會根據設置的優先順序自動管理樣式表在 DOM 中的插入順序,確保樣式表先於內容渲染。
- 伺服器端渲染:React 會確保樣式表在流式渲染過程中提前載入,避免阻塞頁面渲染。
- 客戶端渲染:React 確保樣式表載入完成後再進行渲染,並且多次渲染同一元件時,樣式表只會被載入一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| function ComponentOne() { return ( <Suspense fallback="loading..."> <link rel="stylesheet" href="foo" precedence="default" /> <link rel="stylesheet" href="bar" precedence="high" /> <article className="foo-class bar-class"> {...} </article> </Suspense> ); }
function ComponentTwo() { return ( <div> <p>{...}</p> <link rel="stylesheet" href="baz" precedence="default" /> {/* 將會被插入在 foo 和 bar 之間 */} </div> ); }
function App() { return ( <> <ComponentOne /> ... <ComponentOne /> {/* 不會在 DOM 中導致樣式表鏈接重複 */} </> ); }
|
支援非同步腳本
React 19 在非同步腳本的載入管理上提供了更高效的支援。無論腳本位於元件樹的何處,React 都能確保非同步腳本只會載入一次,從而提升性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| function MyComponent() { return ( <div> <script async={true} src="..." /> Hello World </div> ); }
function App() { return ( <html> <body> <MyComponent /> ... <MyComponent /> {/* 不會導致 DOM 中的腳本重複 */} </body> </html> ); }
|
資源預載入支援
React 19 引入了資源預載入 API,可以讓瀏覽器更早地載入必要的資源,顯著提高頁面性能。這些 API 能夠幫助優化頁面載入速度,並讓資源更快地準備好以應對用戶交互。
- 預載入字體、樣式表等資源:可以在頁面載入之前提前載入字體和樣式表,從而減少頁面顯示的延遲。
- 預先載入導航所需資源:當用戶進行點擊或懸停操作時,預載入未來導航可能需要的資源,加速頁面更新。
1 2 3 4 5 6 7 8 9
| import { prefetchDNS, preconnect, preload, preinit } from "react-dom";
function MyComponent() { preinit("https://.../path/to/some/script.js", { as: "script" }); preload("https://.../path/to/font.woff", { as: "font" }); preload("https://.../path/to/stylesheet.css", { as: "style" }); prefetchDNS("https://..."); preconnect("https://..."); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| <html> <head> <link rel="prefetch-dns" href="https://..." /> <link rel="preconnect" href="https://..." /> <link rel="preload" as="font" href="https://.../path/to/font.woff" /> <link rel="preload" as="style" href="https://.../path/to/stylesheet.css" /> <script async="" src="https://.../path/to/some/script.js"></script> </head> <body> ... </body> </html>
|
React 19 完整支援 web component
React 19 完全支援 Web Components,並解決了以前版本中使用 Web Components 的一些相容性問題。現在,React 能夠正確處理傳遞給自訂元素的屬性,無論是在伺服器端渲染還是客戶端渲染中,都能夠精確區分和處理屬性與自訂元素的實際屬性,這使得 React 更加靈活和強大。
- 伺服器端渲染(SSR):當傳遞給自訂元素的 props 是基礎類型(例如字串、數字或值為 true)時,會作為屬性渲染。如果 props 是非基礎類型(例如物件、符號、函式或值為 false),則會被省略。
- 客戶端渲染(CSR):當 props 與自訂元素實例的屬性相符時,將被作為屬性分配給該元素,否則會作為屬性分配。這樣的設計讓 React 更加靈活地支援自訂元素,解決了先前的相容性問題,無論是在伺服器端還是客戶端渲染中,處理方式都能有效區分屬性和自訂元素的實際屬性。
https://custom-elements-everywhere.com/
React 19 華麗登場,你準備好了嗎
React 19 引入的這些新特性不僅提升了開發者的工作效率,還大幅改善了應用的性能和用戶體驗。
從簡化非同步狀態管理的 useActionState 到提升響應速度的 useOptimistic,再到優化資源載入和對 Web Components 的支援,顯著提高了頁面的載入性能和流暢度。無論是伺服器端渲染還是客戶端渲染,這些改進都能幫助開發者構建更加高效的應用。
如果你是一名 React 開發者,這些新特性無疑會讓你的開發過程更加順利,並能構建出更高效、流暢的應用。
喜歡這篇文章,請幫忙拍拍手喔 🤣