<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    你需要知道關(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、自動(dòng)批處理以減少渲染

    什么是批處理?

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

    例如,如果你在同一個(gè)點(diǎn)擊事件中有兩個(gè)狀態(tài)更新,React 總是將它們分批處理到一個(gè)重新渲染中。如果你運(yùn)行下面的代碼,你會(huì)看到每次點(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 只會(huì)在最后重新渲染一次(這是批處理?。?/span> }
    return ( < div > < button onClick = { handleClick } > Next < / button > < h1 style = { { color : flag ? "blue" : "black" } } > { count } < / h1 > < / div > ) ; }

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

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

    然而,React 的批量更新時(shí)間并不一致。例如,如果你需要獲取數(shù)據(jù),然后更新handleClick上面的狀態(tài),那么 React不會(huì)批量更新,而是執(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 及更早版本不會(huì)對這些進(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 中不會(huì)對 promise、setTimeout、本機(jī)事件處理程序或任何其他事件中的更新進(jìn)行批處理。

    什么是自動(dòng)批處理?

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

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

    我們希望這會(huì)導(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 只會(huì)在最后重新渲染一次(這是批處理?。?/span> }); }
    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 都會(huì)自動(dòng)批量更新,因此:

    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è)用戶啟動(dòng)的事件(如單擊或按鍵),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)用程序中,會(huì)發(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è)步驟。這會(huì)使您的應(yīng)用程序在初始加載時(shí)變慢且無響應(yīng)。

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

    3、startTransition

    什么是過渡?

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

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

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

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

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

    例如,當(dāng)您在下拉列表中選擇過濾器時(shí),您希望過濾器按鈕本身在您單擊時(shí)立即響應(yīng)。但是,實(shí)際結(jié)果可能會(huì)單獨(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òng)作可能會(huì)導(dǎo)致屏幕上發(fā)生很多事情。這可能會(huì)導(dǎo)致頁面在所有工作完成時(shí)凍結(jié)或掛起。

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

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

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

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

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

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

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

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

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

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

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

    這意味著上面的兩個(gè)狀態(tài)仍然會(huì)同時(shí)呈現(xiàn),并且仍然會(huì)阻止用戶看到他們交互的反饋,直到一切都呈現(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)擊或按鍵),則會(huì)中斷。

    如果用戶中斷轉(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é)流和去抖動(dòng)是這種技術(shù)的常見變體。

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

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

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

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

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

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

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

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

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

    我可以在哪里使用它?

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

    • 緩慢渲染:這些更新需要時(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)前的存儲(chǔ)庫升級到最新版本幾乎不需要更改代碼,但我們可以享受它們很酷的功能。

    最后,感謝你的閱讀。

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

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

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

    手機(jī)掃一掃分享

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

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    MD-0127 艾秋 分享骚货老婆 强势调教处男晚辈 | 日本乱婬妺妺躁爽A片 | 青青青青青青草草草草草草草视频 | 日本特一级免费 | 久久影院网红无码视频牛牛夜夜骚 |