<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>

          你需要知道關(guān)于React-18的幾個(gè)新功能

          共 6671字,需瀏覽 14分鐘

           ·

          2021-06-16 12:14

          來源 | https://github.com/reactwg/react-18/discussions

          整理 | 楊小二

          Facebook 團(tuán)隊(duì)已經(jīng)發(fā)布了 React-18 。React 18 提供了許多開箱即用的功能。這些不僅增強(qiáng)了用戶體驗(yàn),而且使開發(fā)人員的生活更輕松。其中,有三個(gè)主要功能值得大家關(guān)注與學(xué)習(xí)了解。

          1、自動批處理以減少渲染

          什么是批處理?

          批處理是 React將多個(gè)狀態(tài)更新分組到單個(gè)重新渲染中以獲得更好的性能。

          例如,如果你在同一個(gè)點(diǎn)擊事件中有兩個(gè)狀態(tài)更新,React 總是將它們分批處理到一個(gè)重新渲染中。如果你運(yùn)行下面的代碼,你會看到每次點(diǎn)擊時(shí),React 只執(zhí)行一次渲染,盡管你設(shè)置了兩次狀態(tài):

          function  App ()  {   const  [ count ,  setCount ]  =  useState ( 0 ) ;   const  [ flag ,  setFlag ]  =  useState ( false ) ;
            function handleClick ( )   setCount ( c => c + 1 ) ; // 還沒有重新渲染 setFlag ( f => ! f ) ; // 還沒有重新渲染 // React 只會在最后重新渲染一次(這是批處理!) }
          return ( < div > < button onClick = { handleClick } > Next < / button > < h1 style = { { color : flag ? "blue" : "black" } } > { count } < / h1 > < / div > ) ; }

          這對性能非常有用,因?yàn)樗苊饬瞬槐匾闹匦落秩尽K€可以防止你的組件呈現(xiàn)僅更新一個(gè)狀態(tài)變量的“半完成”狀態(tài),這可能會導(dǎo)致錯(cuò)誤。

          這可能會讓你想起餐廳服務(wù)員在你選擇第一道菜時(shí)不會跑到廚房,而是等你完成訂單。

          然而,React 的批量更新時(shí)間并不一致。例如,如果你需要獲取數(shù)據(jù),然后更新handleClick上面的狀態(tài),那么 React不會批量更新,而是執(zhí)行兩次獨(dú)立的更新。

          這是因?yàn)?React 過去只在瀏覽器事件(如點(diǎn)擊)期間批量更新,但這里我們在事件已經(jīng)被處理(在 fetch 回調(diào)中)之后更新狀態(tài):

          function App() {  const [count, setCount] = useState(0);  const [flag, setFlag] = useState(false);
          function handleClick() { fetchSomething().then(() => { // React 17 及更早版本不會對這些進(jìn)行批處理,因?yàn)?/span> // 它們在回調(diào)中 *after* 事件運(yùn)行,而不是 *during* 它      setCount ( c  =>  c  +  1 ) ;  // 導(dǎo)致重新渲染      setFlag ( f  =>  ! f ) ;  // 導(dǎo)致重新渲染    } );  }
          return ( <div> <button onClick={handleClick}>Next</button> <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1> </div> );}

          在 React 18 之前,我們只在 React 事件處理程序期間批量更新。默認(rèn)情況下,React 中不會對 promise、setTimeout、本機(jī)事件處理程序或任何其他事件中的更新進(jìn)行批處理。

          什么是自動批處理?

          從 React 18 開始createRoot,所有更新都將自動批處理,無論它們來自何處。

          這意味著超時(shí)、承諾、本機(jī)事件處理程序或任何其他事件內(nèi)的更新將以與 React 事件內(nèi)的更新相同的方式進(jìn)行批處理。

          我們希望這會導(dǎo)致更少的渲染工作,從而在你的應(yīng)用程序中獲得更好的性能:

          function App() {  const [count, setCount] = useState(0);  const [flag, setFlag] = useState(false);
          function handleClick() { fetchSomething().then(() => { // React 18 及更高版本確實(shí)批處理這些: setCount ( c => c + 1 ) ; setFlag ( f => ! f ) ; // React 只會在最后重新渲染一次(這是批處理!) }); }
          return ( <div> <button onClick={handleClick}>Next</button> <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1> </div> );}

          注意:作為采用 React 18 的一部分,預(yù)計(jì)你將升級到createRoot。舊行為的render存在只是為了更容易地對兩個(gè)版本進(jìn)行生產(chǎn)實(shí)驗(yàn)。

          無論更新發(fā)生在何處,React 都會自動批量更新,因此:

          function handleClick() {  setCount(c => c + 1);  setFlag(f => !f);  // React will only re-render once at the end (that's batching!)}

          行為與此相同:

            setTimeout(() => {  setCount(c => c + 1);  setFlag(f => !f);  // React will only re-render once at the end (that's batching!)}, 1000);

          行為與此相同:

          fetch(/*...*/).then(() => {  setCount(c => c + 1);  setFlag(f => !f);  // React will only re-render once at the end (that's batching!)})

          行為與此相同:

          elm.addEventListener('click', () => {  setCount(c => c + 1);  setFlag(f => !f);  // React will only re-render once at the end (that's batching!)});

          注意:React 僅在通常安全的情況下才批量更新。

          例如,React 確保對于每個(gè)用戶啟動的事件(如單擊或按鍵),DOM 在下一個(gè)事件之前完全更新。例如,這可確保在提交時(shí)禁用的表單不能被提交兩次。

          如果我不想批處理怎么辦?

          通常,批處理是安全的,但某些代碼可能依賴于在狀態(tài)更改后立即從 DOM 中讀取某些內(nèi)容。對于這些用例,你可以使用ReactDOM.flushSync()選擇退出批處理:
          import { flushSync } from 'react-dom'; // Note: react-dom, not react
          function handleClick() { flushSync(() => { setCounter(c => c + 1); }); // React has updated the DOM by now flushSync(() => { setFlag(f => !f); }); // React has updated the DOM by now}

          2、Suspense 的 SSR 支持

          這基本上是服務(wù)器端渲染 (SSR) 邏輯的擴(kuò)展。在典型的 React SSR 應(yīng)用程序中,會發(fā)生以下步驟:

          • 服務(wù)器獲取需要在 UI 上顯示的相關(guān)數(shù)據(jù)

          • 服務(wù)器將整個(gè)應(yīng)用程序呈現(xiàn)為 HTML 并將其發(fā)送給客戶端作為響應(yīng)

          • 客戶端下載 JavaScript 包(除了 HTML)

          • 在最后一步,客戶端將 javascript 邏輯連接到 HTML(稱為 hydration)

          典型 SSR 應(yīng)用程序的問題在于,在下一步可以開始之前,必須立即完成整個(gè)應(yīng)用程序的每個(gè)步驟。這會使您的應(yīng)用程序在初始加載時(shí)變慢且無響應(yīng)。

          React 18 正試圖解決這個(gè)問題。<Suspense> 組件已經(jīng)以這樣的方式進(jìn)行了革命性的改變,它將應(yīng)用程序分解為更小的獨(dú)立單元,這些單元經(jīng)過提到的每個(gè)步驟。這樣一旦用戶看到內(nèi)容,它就會變成互動的。

          3、startTransition

          什么是過渡?

          我們將狀態(tài)更新分為兩類:

          • 緊急更新反映直接交互,如打字、懸停、拖動等。

          • 過渡更新將 UI 從一個(gè)視圖過渡到另一個(gè)視圖。

          單擊、懸停、滾動或打字等緊急更新需要立即響應(yīng)以匹配我們對物理對象行為方式的直覺。否則他們會覺得“錯(cuò)了”。

          然而,轉(zhuǎn)換是不同的,因?yàn)橛脩舨幌M谄聊簧峡吹矫總€(gè)中間值。

          例如,當(dāng)您在下拉列表中選擇過濾器時(shí),您希望過濾器按鈕本身在您單擊時(shí)立即響應(yīng)。但是,實(shí)際結(jié)果可能會單獨(dú)轉(zhuǎn)換。

          一個(gè)小的延遲是難以察覺的,而且通常是預(yù)料之中的。如果在結(jié)果渲染完成之前再次更改過濾器,您只關(guān)心看到最新的結(jié)果。

          在典型的 React 應(yīng)用程序中,大多數(shù)更新在概念上都是過渡更新。但出于向后兼容性的原因,過渡是可選的。

          默認(rèn)情況下,React 18 仍然將更新處理為緊急更新,您可以通過將更新包裝到startTransition.

          這解決了什么問題?

          構(gòu)建流暢且響應(yīng)迅速的應(yīng)用程序并不總是那么容易。有時(shí),諸如單擊按鈕或輸入輸入之類的小動作可能會導(dǎo)致屏幕上發(fā)生很多事情。這可能會導(dǎo)致頁面在所有工作完成時(shí)凍結(jié)或掛起。

          例如,考慮在過濾數(shù)據(jù)列表的輸入字段中鍵入。您需要將字段的值存儲在 state 中,以便您可以過濾數(shù)據(jù)并控制該輸入字段的值。您的代碼可能如下所示:

          // 更新輸入值和搜索結(jié)果setSearchQuery ( input ) ;

          在這里,每當(dāng)用戶鍵入一個(gè)字符時(shí),我們都會更新輸入值并使用新值來搜索列表并顯示結(jié)果。

          對于大屏幕更新,這可能會導(dǎo)致頁面在呈現(xiàn)所有內(nèi)容時(shí)出現(xiàn)延遲,從而使打字或其他交互感覺緩慢且無響應(yīng)。

          即使列表不是太長,列表項(xiàng)本身也可能很復(fù)雜并且每次擊鍵時(shí)都不同,并且可能沒有明確的方法來優(yōu)化它們的呈現(xiàn)。

          從概念上講,問題在于需要進(jìn)行兩種不同的更新。第一個(gè)更新是緊急更新,用于更改輸入字段的值,以及可能會更改其周圍的一些 UI。

          第二個(gè)是顯示搜索結(jié)果的不太緊急的更新。

          // 緊急:顯示輸入的內(nèi)容setInputValue ( input ) ;
          // 不急:顯示結(jié)果setSearchQuery ( input ) ;

          用戶希望第一次更新是即時(shí)的,因?yàn)檫@些交互的本機(jī)瀏覽器處理速度很快。但是第二次更新可能會有點(diǎn)延遲。

          用戶不希望它立即完成,這很好,因?yàn)榭赡苡泻芏喙ぷ饕觥#▽?shí)際上,開發(fā)人員經(jīng)常使用去抖動等技術(shù)人為地延遲此類更新。)

          在 React 18 之前,所有更新都被緊急渲染。

          這意味著上面的兩個(gè)狀態(tài)仍然會同時(shí)呈現(xiàn),并且仍然會阻止用戶看到他們交互的反饋,直到一切都呈現(xiàn)出來。我們?nèi)鄙俚氖且环N告訴 React 哪些更新是緊急的,哪些不是的方法。

          新startTransitionAPI 通過讓您能夠?qū)⒏聵?biāo)記為“轉(zhuǎn)換”來解決此問題:

          import  {  startTransition  }  from  'react' ;
          // 緊急:顯示輸入的內(nèi)容setInputValue ( input ) ;
          // 將內(nèi)部的任何狀態(tài)更新標(biāo)記為轉(zhuǎn)換startTransition ( ( ) => { // Transition: 顯示結(jié)果 setSearchQuery ( input ) ; } ) ;

          包裝在其中的更新startTransition被視為非緊急處理,如果出現(xiàn)更緊急的更新(如點(diǎn)擊或按鍵),則會中斷。

          如果用戶中斷轉(zhuǎn)換(例如,連續(xù)輸入多個(gè)字符),React 將拋出未完成的陳舊渲染工作,僅渲染最新更新。

          Transitions 可讓您保持大多數(shù)交互敏捷,即使它們導(dǎo)致顯著的 UI 更改。它們還可以讓您避免浪費(fèi)時(shí)間渲染不再相關(guān)的內(nèi)容。

          它與 setTimeout 有何不同?

          上述問題的一個(gè)常見解決方案是將第二次更新包裝在 setTimeout 中:

          // 顯示你輸入的內(nèi)容setInputValue ( input ) ;
          // 顯示結(jié)果setTimeout ( ( ) => { setSearchQuery ( input ) ; } , 0 ) ;

          這將延遲第二次更新,直到呈現(xiàn)第一次更新之后。節(jié)流和去抖動是這種技術(shù)的常見變體。

          一個(gè)重要的區(qū)別是startTransition不安排在以后喜歡的setTimeout是。它立即執(zhí)行。傳遞給的函數(shù)startTransition同步運(yùn)行,但其中的任何更新都標(biāo)記為“轉(zhuǎn)換”。

          React 將在稍后處理更新時(shí)使用此信息來決定如何呈現(xiàn)更新。這意味著我們比在超時(shí)中包裝更新更早地開始呈現(xiàn)更新。

          在快速設(shè)備上,兩次更新之間的延遲非常小。在較慢的設(shè)備上,延遲會更大,但 UI 會保持響應(yīng)。

          另一個(gè)重要的區(qū)別是 a 內(nèi)的大屏幕更新setTimeout仍然會鎖定頁面,只是在超時(shí)之后。

          如果用戶在超時(shí)觸發(fā)時(shí)仍在鍵入或與頁面交互,他們?nèi)詫⒈蛔柚古c頁面交互。但是標(biāo)記為 的狀態(tài)更新startTransition是可中斷的,因此它們不會鎖定頁面。

          它們讓瀏覽器在呈現(xiàn)不同組件之間的小間隙中處理事件。

          如果用戶輸入發(fā)生變化,React 將不必繼續(xù)渲染用戶不再感興趣的內(nèi)容。

          最后,因?yàn)閟etTimeout只是延遲更新,顯示加載指示器需要編寫異步代碼,這通常很脆弱。

          通過轉(zhuǎn)換,React 可以為您跟蹤掛起狀態(tài),根據(jù)轉(zhuǎn)換的當(dāng)前狀態(tài)更新它,并讓您能夠在用戶等待時(shí)顯示加載反饋。

          我可以在哪里使用它?

          您可以使用startTransition來包裝要移動到后臺的任何更新。通常,這些類型的更新分為兩類:

          • 緩慢渲染:這些更新需要時(shí)間,因?yàn)?React 需要執(zhí)行大量工作才能轉(zhuǎn)換 UI 以顯示結(jié)果。

          • 慢速網(wǎng)絡(luò):這些更新需要時(shí)間,因?yàn)?React 正在等待來自網(wǎng)絡(luò)的一些數(shù)據(jù)。此用例與 Suspense 緊密集成。

          總結(jié)

          React 18 沒有任何重大更改,因此,我們將當(dāng)前的存儲庫升級到最新版本幾乎不需要更改代碼,但我們可以享受它們很酷的功能。

          最后,感謝你的閱讀。

          學(xué)習(xí)更多技能

          請點(diǎn)擊下方公眾號

          瀏覽 31
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  成人在线免费视频观看 | 欧美 日本 国产 | 亚洲在钱免费观看 | 亚洲成人色图专区 | 一级爱爱片|