天天看點

React 18 之畫師登仙

我莫名地發現自己坐在一座廟宇的大殿中,周圍的人有老有小、衣着各異,但似乎人人都眼神木讷、正襟危坐。

這是個什麼鬼地方?

我正四處打量,忽然有人輕輕敲擊我的後背,我轉過身去,看到一個大概二十出頭的女孩。她俏皮地笑了笑,把食指豎在唇前,示意我不要出聲。順着她手指的方向,透過前排的間隙,我向大殿中央望去。

隻見那裡端坐一人,身披黃色錦袍,右手握着一支一米多長的碩大毛筆,正在面前的畫布上運筆如飛。

我想,敢情是網紅畫家現場表演?怪不得那麼多人圍觀。

忽然,一旁的助手将另一幅畫布放在黃衣人面前,他也不停筆,伸出另一隻手,用一支一模一樣的毛筆在新的畫布上開始作畫。

一心二用、左圓右方啊,有兩把刷子!你到底畫的是啥?

正尋思間,助手在黃衣人面前加了一幅畫布,隻見他雙手仍然不停筆,竟從錦袍中伸出第三隻手,拿着毛筆在這第三幅畫布上作畫。

我确信沒看錯!他确實有三隻手,拿着三支筆!我驚魂未定,助手卻又拿出三幅畫布,盡數都擺在畫家面前。

難道他是三頭六臂?我不禁倒吸一口氣。

果不出所料,畫家又多出三隻手,六手六筆在六幅畫布上同時作畫,沒有絲毫地猶豫和停頓。

不過這次我看清楚了,他多餘的手并不是從錦袍裡伸出來的,而是原來的手稍微晃動了一下,在虛影之中不知如何就“分裂”出一隻手來,拿着一支同樣的毛筆。

這是什麼情況?忙于對眼前的景象作出合了解釋,我的大腦差點當機。

隻見畫家微微點頭,最邊上的一隻手提筆輕輕一頓,似乎是完成了該幅畫作。那隻手微微晃動了一下,又是一陣虛影,竟然和旁邊的一隻手合而為一。

在我的瞠目結舌中,畫家另外的四隻手也紛紛完成畫作,虛影之下四合二、二合一,最後隻留下形單影隻的右手。一陣清風拂過,錦袍空空的左袖随風晃動。

畫家随即将手中的畫筆淩空一擲,助手穩穩接住,雙手高舉過頭,朗聲道:“十八紀元,畫師登仙。” 我看見周圍衆人紛紛拜倒在地。

“其實他就是動作快而已,明明隻有一隻手卻畫出了六隻手的效果。” 那女孩不知什麼時候擠到我身旁,悄聲告訴我。“快行禮啊,被畫師看見了可不好。” 我這才反應過來,忙不疊地模仿旁人的姿勢,但已經太晚。畫師目光如炬,早已注意到了我這個不和諧的音符,助手則右手運力,将毛筆如标槍一般朝我擲來。

我大驚之餘,急忙躲避,頭卻撞到旁邊不知什麼硬物,随即發現自己正趴在家裡書桌上,并沒有什麼廟宇大殿。在我面前也并沒有什麼畫師,而隻是電腦螢幕和一本書,封面上寫着《坐标 React 星:React 核心思維模型》。

我這才記起來,剛才是在趕一篇關于 React 18 的文章(剛剛釋出的 React 最新版本),實在太累就趴在桌上休息了一會。我回想起剛才的夢境,看着桌上那本書,那是一本我剛剛整理完成的 React 教程,包含了很多這樣的故事橋段。我不禁笑了起來,這是入戲太深啊……

React 18 之畫師登仙

不過話說回來,這獨臂畫師跟 React 18 确實有很多相通之處。React 18 堪稱 React 發展史上的一個裡程碑,它的核心是所謂的“并發渲染”(Concurrent rendering)。

React 18核心:并發渲染(Concurrent rendering)

什麼是并發渲染呢?這得從 React 18 之前版本的元件渲染機制說起。

React 渲染曾經是一個必須從頭到尾完整執行的過程,一旦開始渲染就不能中斷,要等到将界面效果呈現給使用者以後才能開始下一輪。

這意味着什麼呢?

我們知道 JavaScript 從本質上講是單線程的,React 運作在浏覽器的主線程之上,在這個單一的線程上還有很多代碼排隊執行,包括了對使用者體驗至關重要的使用者輸入處理、頁面布局、動畫等等。如果 React 的渲染代碼執行時間太長,排在後面的其它代碼便無法及時運作。是以,當渲染複雜元件時,使用者界面常常會出現卡頓的現象。這好比要求我們的獨臂畫師必須完成一幅畫以後才可以做其他事,停下來喝口水都不行。

不過,現在到了十八紀元,動作奇快的畫師得以大顯神威。

在 React 18 裡,元件渲染過程變得可以暫停、可以從斷點恢複、甚至可以丢棄一些過時中間結果,這樣,不管元件有多複雜,其渲染過程也不會阻塞主線程。React 可以在一個元件上先畫幾筆,這時如果有更高優先級的任務(例如處理使用者輸入),或者因為某種原因需要等待(例如下載下傳資料),React 就暫停這個元件的渲染,轉而處理其他任務,在某個時刻再回過頭來繼續渲染原來的元件,因為切換的速度很快,從宏觀上看來就是三頭六臂、多個任務同時執行了!

React 18 之畫師登仙

(圖檔來源:Beebee老師)

并發渲染聽起來很強大吧?那麼作為開發者,我們怎麼才能用到它的三頭六臂呢?

事實上,并發渲染并非面向應用開發者的 API,它是 React 18 的底層工作機制,算是一種實作細節。不過,它是 React 18中(以及未來版本)很多新功能的基礎。了解了并發渲染的工作原理,我們将會更加得心應手,是以我才在它上面花了這麼長的篇幅。

下面挑兩個 React 18 中最典型的新功能為你簡略介紹一下,它們分别是——Transition 和 Suspense。

Transition

Transition 相關 API 的目标是無縫支援不同配置的運作環境,旨在消除使用者界面在慢速機器上的卡頓,而在高性能環境中又能充分地利用計算資源、快速更新界面和顯示更豐富的内容。

為了達到上述目标,React 18 需要我們幫助區分緊急和非緊急的界面更新。

所謂緊急更新,是指那些直接銜接使用者互動的界面更新,例如顯示鍵盤輸入内容、滑鼠點選回報等等。如果此類界面更新沒有得到及時處理,哪怕隻是延遲了幾百毫秒,使用者就會感到很明顯的卡頓。

而非緊急更新則是指那些即使是延遲處理也不會讓人感到意外的界面更新,例如,根據下載下傳的資料顯示圖表,即使慢個幾秒問題也不大,此類更新也被稱為 Transition。

正是因為并發渲染,React 可以“不緊不慢地”運作非緊急更新,而當使用者輸入到來時暫停手上的工作并立即處理緊急更新。這樣,我們既在慢速機器上保證了界面的響應速度,又能在高性能環境中快速更新界面、顯示更豐富的内容。類似的需求如果要“手工”來實作将會相當麻煩,效果還不一定理想。而在 React 18 裡,我們隻需要标記好非緊急更新,具體的更新速度和顯示内容由 React 根據機器速度自動調節,對于開發者來說是完全透明的。

那麼,怎麼标記非緊急更新呢?

React 提供了一個 startTransition 函數(另外還有一個 useTransition Hook,用法類似,在此略去):

startTransition( () => {
   // 在此處放入非緊急更新,如 setData(...)。凡是不在此的更新均預設為緊急更新。
})           

如下是一個 Slider 元件的示例:

function FastSlider({ onChange }) {
  const [value, setValue] = useState(1);
  return (
    <input  type="range" value={value}
      onChange={(e) => {
        const v = e.target.valueAsNumber;
        setValue(v); // ==> 這是緊急更新,因為是對使用者輸入的回報
        startTransition(() => onChange(v)); // 這是非緊急更新,放到 startTransition 函數裡讓 React “慢慢”執行。
      }}
    />
  );
}           

是以,标記非緊急更新是很容易的,放到 startTransition 調用裡就行了。React 将預設其它的更新為緊急更新。

Suspense

Suspense 是 React 18 提供的一個元件,可以用來以聲明式的方式定義進度資訊。具體用法見如下代碼:

function MyComponent() {
  return (
    <Suspense fallback={<div>努力加載中...</div>}>
       <ComponentThatLoadsData />
    </Suspense>
  )
}           
React 18 之畫師登仙

在上述代碼裡,當 ComponentThatLoadsData 開始下載下傳資料時,對于該元件的渲染過程就會暫停(Suspend),Suspense 元件将接管并渲染 fallback 中的進度資訊。

此時,使用者将在浏覽器中看到“努力加載中...”。等到資料下載下傳完成,React 将繼續渲染 ComponentThatLoadsData,顯示最終結果。跟 Transition 類似,同樣是并發渲染的三頭六臂在這裡起到了至關重要的作用。

你可能會問,用這種方式定義進度條到底有什麼優勢呢?以前用一個 loading state 加條件渲染的方式不是挺好嗎?

這一點以後有時間我再撰文詳述。

簡言之,Suspense 将所有異步操作的這一部分邏輯抽象出來,把等待狀态作為 JSX 裡的“一等公民”,這樣便于實作開發、設計工具支援,以及與設計師合作。畢竟,React 是以全面提高使用者體驗為目标的,與整個開發生态系統的協作是很重要的。

另外,React 18 中的 Suspense 在伺服器端也有良好支援。同樣是得益于并發渲染,伺服器能夠以流(stream)的方式為用戶端提供渲染結果,這樣就完美支援了包含異步操作的元件的 伺服器端渲染(Server-side Rendering,SSR)。伺服器能夠先渲染元件的一部分,将包含進度資訊的頁面提供給用戶端,等到暫停的元件準備就緒再提供剩餘的渲染結果。

總結

React 18 以及畫師的故事就講到這裡了。并發渲染是 React 前進的一大步,是 React 核心開發團隊曆經多年的研究、實驗和開發的成果。我認為其必将對 React 以及整個前端開發領域的發展起到意義深遠的作用。

不過,如果你還是 React 新手,是不是覺得有太多東西需要學了?什麼元件、渲染,又加上 Transition、Suspense…… 告訴你一個好消息,你可以完全暫時不用管上面的代碼和技術細節,記住獨臂畫師的六隻手就夠了。等到你掌握了 React 的基礎知識,再回過頭來一定會豁然開朗!

如果你喜歡這個關于畫師的夢,我把《坐标 React 星:React 核心思維模型》這本書推薦給你。在書裡,我用相同的手法以故事的形式講解 React 的基礎知識,讓你在娛樂中學習。對了,畫師在書裡有出場哦!

React 18 之畫師登仙
React 18 之畫師登仙
React 18 之畫師登仙
React 18 之畫師登仙
React 18 之畫師登仙
React 18 之畫師登仙

下單即減50,快快掃碼搶購吧!

當當423福利來啦!

KGHCJ9 (20元優惠碼)

當當每滿100-50 再疊加20元優惠碼

實付100可用 花80元買原價200元的書

React 18 之畫師登仙