【轉】詳解 React 18 更新后的特性


React 18 通過其改進的渲染系統帶來了并發能力,并在此基礎上構建了轉換或自動批處理等性能增強特性。下面就看看到底有哪些值得關注的新特性。

迭代更新內容

總的來說,由于新的并發特性是漸進適配并按需啟用的,React 18 中的重大更改僅限于幾個簡單的 API 更改,以及對 React 中多個行為的穩定性和一致性的一些改進,比較重要的一點是,不再支持 IE 瀏覽器。

1、客戶端渲染 API

帶有 createRoot() 的 root API,替換現有的 render() 函數,提供更好的人體工程學并啟用新的并發渲染特性。

import { createRoot } from "react-dom/client";
import App from "App";

const container = document.getElementById("app");
const root = createRoot(container);
root.render(<App />);

請注意,這個新的 API 現在已從 react-dom/client 模塊導出,卸載和水合 API 也發生了變化。

// Unmount component at DOM node:
// ...
root.unmount();

// Hydration
import { hydrateRoot } from "react-dom/client";
// ...

const container = document.getElementById("app");
const root = hydrateRoot(container, <App tab="home" />);

由于使用 Suspense 時會出現不正確 timing 的問題,渲染回調已經一去不復返了。替代選擇是頂部組件內部的一個效果:

import { createRoot } from "react-dom/client";
import { useEffect } from "react";
import App from "App";

const App = () => {
  useEffect(() => {
    console.log("render callback");
  });

  return <div></div>;
};
const container = document.getElementById("app");
const root = createRoot(container);
root.render(<App />);
2、自動批處理

createRoot() API 還是 React 18 中另一個改進的入口——自動批處理。在 React 的早期版本中,狀態更新在 React 事件偵聽器中完成時已經批量處理了,以優化性能并避免重渲染。從 React 18 開始,狀態更新也將被安排到其他地方——比如在 Promise、setTimeout 回調和原生事件處理程序中。

const App = () => {
  const handleClick = () => {
    setA((a) => a + 1);
    setB((b) => b - 1);
    // Updates batched - single re-render
  };

  setTimeout(() => {
    setA((a) => a + 1);
    setB((b) => b - 1);
    // New (v18): Updates batched - single re-render
  }, 1000);

  // ...
};

這個更改雖然一般來說符合人們期望,也挺有用,但可能是破壞性的。如果你的代碼依賴于在分開的狀態更新之間重渲染的組件,那么你必須使其適應新的批處理機制,或使用 flushSync() 函數來強制立即刷新更改。

import { flushSync } from "react-dom";
// ...

const handleClick = () => {
  flushSync(() => {
    setA((a) => a + 1);
  });
  // Re-render
  flushSync(() => {
    setB((b) => b - 1);
  });
  // Re-render
};
3、嚴格模式更新

React 18 帶來了大把新特性,此外還有很多新特性正在路上。為了讓你的代碼為此做好準備,StrictMode 變得更加嚴格了。最重要的是,StrictMode 將測試組件對可重用狀態的彈性,模擬一系列的掛載和卸載行為。它旨在讓你的代碼為即將推出的特性(可能以組件的形式)做好準備,這將在組件的掛載周期中保留這個狀態。

雖然它肯定會在未來提供更好的性能,但就目前而言,啟用 StrictMode 時必須要考慮這個事情。

除了以上提到的更改之外,根據你的 React 代碼庫,你可能還會發現其他一些更改。

值得一提的是,React 18 將不再支持 IE 瀏覽器,因為 React 18 現在依賴很多現代瀏覽器特性,如 Promise 或 Object.assign。

其余的更改與一些 React 行為的穩定性和一致性有關,不太可能影響你的代碼庫。

4、并發的 React

并發渲染器是 React 渲染系統的一項幕后特性。它允許并發渲染,即同時在后臺準備多個版本的 UI。這意味著更好的性能和更平滑的狀態轉換。

雖然并發似乎只是一個實現細節,但其實它是大多數新特性的動力源泉。事實上,只有當你使用其中一種特性(如 transition、Suspense 或流式 SSR)時,才會啟用并發渲染。這就是為什么了解并發渲染的工作機制是非常重要的。

5、Transition

Transition 是由并發渲染提供支持的新特性之一。它旨在與現有狀態管理 API 一起使用,以區分緊急和非緊急狀態更新。通過這種方式,React 知道哪些更新需要優先考慮,哪些更新需要在后臺通過并發渲染準備。

要知道何時使用 transition,你必須更好地了解用戶是如何與你的應交互的。例如,在字段中鍵入或單擊按鈕是用戶期望立即獲得響應的操作——響應可能是出現在文本字段中的一個值,或是要打開的某個菜單。但對于搜索、加載或處理數據(例如搜索欄、圖表、過濾表等)這些事情,用戶也會期望它們需要一些時間來完成。后者就是你使用 transition 的場景了。

可以使用 useTransition() 鉤子來創建一個 transition。這個鉤子返回一個函數來啟動一個 transition,還有一個掛起的指示器來通知你 transition 的進度。

import { useTransition, useState } from "react";

const App = () => {
  const [isPending, startTransition] = useTransition();
  const [value, setValue] = useState(0);

  function handleClick() {
    startTransition(() => {
      setValue((value) => value + 1);
    });
  }

  return (
    <div>
      {isPending && <Loader />}
      <button onClick={handleClick}>{value}</button>
    </div>
  );
};

在 startTransition() 回調中提交的任何狀態更新都將被標記為 transition,從而使其他更新具有優先權。如果你不能使用這個鉤子,還有一個單獨的 startTransition() 函數可用——雖然它不會通知你轉換的進度。

import { startTransition } from "react";
// ...
startTransition(() => {
  // Transition updates
});
// ...
6、Suspense 更新

與 React Suspense 結合使用時,transition 的效果是最好的。由于一些改進,Suspense 現在可以很好地與并發渲染集成、在服務器上工作,并且可能很快支持 lazy() 加載組件之外的用例。與 transition 一起使用時,Suspense 將避免隱藏現有內容??紤]以下示例:

import { Suspense } from "react";
// ...

const App = () => {
  const [value, setValue] = useState("a");
  const handleClick = () => {
    setValue("b");
  };

  return (
    <>
      <Suspense fallback={<Loader />}>
        {value === "a" ? <A /> : <B />}
      </Suspense>
      <Button onClick={handleClick}>B</Button>
    </>
  );
};

在狀態改變時,lazy() 加載的組件將觸發 Suspense,導致 fallback 元素的渲染。如果你將狀態更改標記為一個 transition,React 將知道它應該在后臺準備新視圖,同時仍保持當前視圖可見。

import { Suspense, useTransition } from "react";
// ...

const App = () => {
  const [value, setValue] = useState("a");
  const [isPending, startTransition] = useTransition();
  const handleClick = () => {
    startTransition(() => {
      setValue("b");
    });
  };

  return (
    <>
      <Suspense fallback={<Loader />}>
        <div style={{ opacity: isPending ? 0.7 : 1 }}>
          {value === "a" ? <A /> : <B />}
        </div>
      </Suspense>
      <Button onClick={handleClick}>B</Button>
    </>
  );
};

現在,即使在處理 transition 時視圖不會改變,你仍然可以使用過渡指示器來向用戶提供反饋,例如設置容器不透明度。將上述改進與未來 Suspense 的新能力(與 lazy() 加載的組件之外的異步任務一起使用)相結合,意味著 Suspense 將成為 React 最強大的特性之一。

7、服務端渲染改進

除了 Suspense 支持之外,React 的 SSR 方面還有很多其他變化。將 Suspense 與 SSR 流式傳輸和懶惰水合(lazy hydration)相結合,意味著你的服務端渲染應用將盡快水合并可用。不僅如此,零打包體積的服務端組件即將到來。它們目前處于試驗階段,但可能會在以后的次要版本中進入穩定狀態。使用它們時,你將能減少提供給客戶端的 JS 代碼,甚至進一步優化 React 應用程序的性能和加載時間。

React 17 的多個更改,即使你的代碼庫很大,你也應該能夠輕松地逐步采用 React 18。你不僅可以在應用程序的選定部分中使用新版本,還可以從 render() 遷移到 createRoot(),來一步步選擇加入新的特性和行為。最重要的是,即使使用的是 createRoot(),你仍然可以逐步采用并發渲染,因為它只有在你使用它的特性時才會啟用??傮w而言,遷移過程應該很順利。

對 React 的展望
React 18 帶來了許多新特性,可以看到一些即將出現的新事物。服務器組件、用于數據獲取的 Suspense,和組件渲染都是接下來的新特性的一部分。React 正在與它的整個生態系統一起發展,迫不及待地想看看接下來會發生什么。

翻譯:https://blog.openreplay.com/react-18-features-breakdown

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,362評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,013評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,346評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,421評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,146評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,534評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,585評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,767評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,318評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,074評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,258評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,828評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,486評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,916評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,156評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,993評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,234評論 2 375

推薦閱讀更多精彩內容