<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          React 并發(fā) API 實戰(zhàn),這幾個例子看懂你就明白了

          共 6595字,需瀏覽 14分鐘

           ·

          2024-04-11 23:29

          本文適合對React 并發(fā) API感興趣的小伙伴閱讀。

          歡迎關注前端早茶,與廣東靚仔攜手共同進階~

          目錄

          • 什么是并發(fā)
          • 它和 React 有什么關系
          • 中斷和切換是如何工作的
          • 那 Suspense 呢?
          • 如何啟動 transition
          • 結束語

          什么是并發(fā)

          并發(fā)是一種執(zhí)行模型,它允許程序的不同部分可以不按順序執(zhí)行,而不影響最終結果。你可能聽說過多線程或多進程。由于瀏覽器中的 JavaScript 只能訪問一個線程(雖然 Web Workers 在單獨的線程中運行,但它們和 React 關系不大),我們不能使用多線程來并行處理一些計算。為了確保資源的最佳利用和頁面的響應性,JavaScript 必須采用不同的并發(fā)模型:協(xié)作式多任務。這聽起來可能有點復雜,但別擔心,你已經熟悉這個模型了,而且肯定用過。

          它和 React 有什么關系

          在 React 18 之前,React 中的所有更新都是同步的。如果 React 開始處理一個更新,它會完成它,不管你在干嘛(當然,除非你關閉了標簽頁)。即使這意味著忽略了此時發(fā)生的用戶事件,或者如果你有一些特別重的組件,頁面會凍結。對于較小的更新來說,這還好,但對于涉及渲染大量組件的更新(比如路由變化),它對用戶體驗產生了負面影響。

          React 18 引入了兩種類型的更新:緊急狀態(tài)更新和 transition 狀態(tài)更新。默認情況下,所有狀態(tài)更新都是緊急的,這樣的更新不能被中斷。transition 是低優(yōu)先級的更新,可以被中斷。從現在起,我也將使用“高優(yōu)先級更新”和“低優(yōu)先級更新”來指代它們。

          為了保持向后兼容性,默認情況下,React 18 的行為和之前的版本一樣,所有更新都是高優(yōu)先級的,因此不可中斷。要啟用并發(fā)渲染,你需要通過使用startTransitionuseDeferredValue將更新標記為低優(yōu)先級。

          中斷和切換是如何工作的

          在渲染低優(yōu)先級更新時,React 在渲染完每個組件后會暫停,并檢查是否有高優(yōu)先級更新需要處理。如果有,React 會暫停當前渲染,切換到渲染高優(yōu)先級更新。處理完這些后,React 會返回到渲染低優(yōu)先級更新(或者如果它無效了,就丟棄它)。除了高優(yōu)先級更新,React 還會檢查當前渲染是否耗時過長。如果耗時過長,React 會將控制權還給瀏覽器,以便它可以重繪屏幕,避免卡頓和凍結。

          由于 React 只能在組件之間暫停(它不能在組件中間停下來),所以如果你有一兩個特別重的組件,并發(fā)渲染幫助不大。如果組件渲染需要 300 毫秒,瀏覽器就會被阻塞 300 毫秒。并發(fā)渲染真正發(fā)揮作用的地方是當你的組件只是稍微慢一點,但它們的數量比較多,以至于總渲染時間相當長。

          那 Suspense 呢?

          你可能聽說過 CPU 密集型程序。這類程序大多數時間都在積極地使用 CPU 來完成它們的工作。我們之前提到的慢組件可以歸類為 CPU 密集型:為了更快地渲染,它們需要更多的資源。

          與 CPU 密集型程序相反,還有 I/O 密集型程序。這類程序大部分時間都在與輸入輸出設備(比如磁盤或網絡)交互。在 React 中負責處理 I/O 的組件是 Suspense。

          如果組件在低優(yōu)先級更新期間暫停,Suspense 的行為會有所不同。如果 Suspense 邊界內已經有內容顯示,React 不會像通常那樣處理暫停并顯示 fallback 內容,而是會暫停渲染,轉而處理其他任務,直到 Promise resolved,然后提交一個帶有新內容的完整子樹。這樣,React 避免了隱藏已經顯示的內容。如果組件在首次渲染期間暫停,將顯示 fallback 內容。

          如何啟動 transition

          啟動 transition 有幾種方法,最基本的是startTransition函數。你像這樣使用它:

          import { startTransition, useState } from 'react'

          const StartTransitionUsage = () => {
            const onInputChange = (value: string) => {
              setInputValue(value)
              startTransition(() => {
                setSearchQuery(value)
              })
            }

            const [inputValue, setInputValue] = useState('')
            const [searchQuery, setSearchQuery] = useState('')

            return (
              <div>
                <SectionHeader title="Movies" />
                <input placeholder="Search" value={inputValue} onChange={(e) => onInputChange(e.target.value)} />
                <MoviesCatalog searchQuery={searchQuery} />
              </div>

            )
          }

          這里發(fā)生的事情是,當用戶在搜索輸入框中輸入時,我們像往常一樣更新狀態(tài)變量inputValue,然后調用startTransition,傳入一個包含另一個狀態(tài)更新的函數。這個函數會立即被調用,React 會記錄其執(zhí)行期間所做的任何狀態(tài)更改,并將它們標記為低優(yōu)先級更新。請注意,至少在 React 18.2 中,只能傳遞同步函數給startTransition。

          所以在我們的示例中,我們實際上啟動了兩個更新:一個是緊急的(更新inputValue),另一個是 transition(更新searchQuery)。MoviesCatalog組件可能會使用 Suspense 來根據搜索查詢獲取電影,這將使該組件成為 I/O 密集型。此外,它還可以渲染相當長的一系列電影卡片,這可能使它也成為 CPU 密集型。有了 transition,這個組件在加載數據時不會觸發(fā) Suspense fallback(會顯示過時的 UI),在渲染長列表的電影卡片時也不會卡住瀏覽器。

          需要注意的是,在 CPU 密集型組件的情況下,它們應該用React.memo包裹起來,否則即使它們的 props 沒有變化,它們也會在每次高優(yōu)先級渲染時重新渲染,這會影響你應用的性能。

          startTransition是最基礎的函數,主要用于 React 組件之外。要從 React 組件內部啟動 transition,我們有一個更酷的版本:useTransitionhook。

          import { useTransition, useState } from 'react'

          const UseTransitionUsage = () => {
            const onInputChange = (value: string) => {
              setInputValue(value)
              startTransition(() => {
                setSearchQuery(value)
              })
            }
            const [inputValue, setInputValue] = useState('')
            const [searchQuery, setSearchQuery] = useState('')
            const [isPending, startTransition] = useTransition()

            return (
              <div>
                <SectionHeader title="Movies" isLoading={isPending} />
                <input placeholder="Search" value={inputValue} onChange={(e) => onInputChange(e.target.value)} />
                <MoviesCatalog searchQuery={searchQuery} />
              </div>

            )
          }

          有了這個 hook,你不需要直接導入startTransition;相反,你調用useTransition()hook,它會返回一個包含兩個元素的數組:一個 boolean 值,表示是否有任何低優(yōu)先級更新正在進行(從這個組件發(fā)起),以及你用來啟動 transition 的startTransition函數。

          當你以這種方式啟動 transition 時,React 實際上會進行兩次渲染:一次高優(yōu)先級渲染,將isPending翻轉為 true,以及一次低優(yōu)先級更新,包含你傳遞給startTransition的實際狀態(tài)更改。所以要小心,用React.memo包裹“昂貴”的組件。

          我們還有另一個新 hook 是useDeferredValue。如果相同的狀態(tài)在關鍵和重型組件中都使用,它就變得有用了。就像我們上面的例子一樣。多方便???這是你如何使用它:

          import { useDeferredValue, useState } from 'react'

          const UseDeferredValueUsage = () => {
            const [inputValue, setInputValue] = useState('')
            const searchQuery = useDeferredValue(inputValue)

            return (
              <div>
                <SectionHeader title="Movies" isLoading={inputValue !== searchQuery} />
                <input placeholder="Search" value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
                <MoviesCatalog searchQuery={searchQuery} />
              </div>

            )
          }

          在低優(yōu)先級渲染中,和高優(yōu)先級首次渲染中,useDeferredValue會存儲傳遞的值,并立即返回它,所以inputValuesearchQuery將是相同的字符串。但在隨后的高優(yōu)先級渲染中,React 總是返回存儲的值。但它也會比較你傳遞的值和存儲的值,如果它們不同,React 會安排一個低優(yōu)先級更新。如果在低優(yōu)先級等待更新時,高優(yōu)先級這時更新了,值再次變化,React 會丟棄它,并安排一個帶有最新值的新的低優(yōu)先級更新。

          使用這個 hook,你可以擁有同一狀態(tài)的兩個版本:一個用于關鍵組件,比如輸入字段(通常不能接受延遲),另一個用于像搜索結果這樣的組件(用戶習慣了更長的延遲)。

          結束語

          并發(fā)無疑是一個有趣的特性,可以確定一些復雜的應用會從中受益。更重要的是,它可能已經在你最喜歡的 React 框架的底層使用了(Remix 和 Next 都已經把它用于路由)。我懷疑一旦數據獲取的 Suspense 達到生產就緒的狀態(tài),它就會更受歡迎。但現在,你還有時間學習并逐漸將其采用到你的應用中。如果你想更詳細地了解 React 中的并發(fā),并了解一些歷史背景,可以看看 Ivan Akulov 的這個演講[1]

          關注我,一起攜手進階

          歡迎關注前端早茶,與廣東靚仔攜手共同進階~

          參考資料

          [1]

          Ivan Akulov 的這個演講: https://3perf.com/talks/react-concurrency/

          [2]

          react18并發(fā)指導: https://sinja.io/blog/guide-to-concurrency-in-react-18#what-is-concurrency

          瀏覽 28
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  丁香色婷婷五月天 | 国产V亚洲V日韩V欧美V | 大香蕉啪啪啪啪啪啪啪啪啪 | 国产婷婷激情综合 | 豆花精品无码 |