<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如何原生實現(xiàn)防抖?

          共 3859字,需瀏覽 8分鐘

           ·

          2022-05-20 21:04

          作者:卡頌

          介:《React技術(shù)揭秘》作者

          來源:SegmentFault  思否社區(qū) 


          大家好,我卡頌。


          作為前端,想必你對防抖(debounce)、節(jié)流(throttle)這兩個概念不陌生。


          在React18中,基于新的并發(fā)特性,React原生實現(xiàn)了防抖的功能。


          今天我們來聊聊這是如何實現(xiàn)的。


          useTransition Demo



          useTransition是一個新增的原生Hook,用于以較低優(yōu)先級執(zhí)行一些更新。


          在我們的Demo中有ctnnum兩個狀態(tài),其中ctn與輸入框的內(nèi)容受控。


          當觸發(fā)輸入框onChange事件時,會同時觸發(fā)ctnnum狀態(tài)變化。其中觸發(fā)num狀態(tài)變化的方法(即updateNum)被包裹在startTransition中:


          function App() {
            const [ctn, updateCtn] = useState('');
            const [num, updateNum] = useState(0);
            const [isPending, startTransition] = useTransition();

            return (
              <div >
                <input value={ctn} onChange={({target: {value}}) => {
                  updateCtn(value);
                  startTransition(() => updateNum(num + 1))
                }}/>
                  <BusyChild num={num}/>
              </div>
            );
          }


          num會作為props傳遞給BusyChild組件。在BusyChild中通過while循環(huán)人為增加組件render所消耗的時間:


          const BusyChild = React.memo(({num}: {num: number}) => {
            const cur = performance.now();
            // 增加render的耗時
            while (performance.now() - cur < 300) {}

            return <div>{num}</div>;
          })


          所以,在輸入框輸入內(nèi)容時能明顯感到卡頓。



          在線示例地址:https://codesandbox.io/s/immutable-glade-u0m6vv


          按理說,onChange中會同時觸發(fā)ctnnum的狀態(tài)變化,他們在視圖中的顯示應(yīng)該是同步的。


          然而實際上,輸入框連續(xù)輸入一段文字(即ctn的狀態(tài)變化連續(xù)展示在視圖中)后,num才會變化一次。


          如下圖,初始時輸入框沒有內(nèi)容,num為0:



          輸入框輸入很長一段文字后,num才變?yōu)?:



          這種效果就像:被startTransition包裹的更新都有防抖的效果一樣。


          這是如何實現(xiàn)的呢?


          什么是lane



          React18中有一套更新優(yōu)先級機制,不同地方觸發(fā)的更新?lián)碛胁煌瑑?yōu)先級。優(yōu)先級的定義依據(jù)是符合用戶感知的,比如:


          • 用戶不希望輸入框輸入文字會有卡頓,所以onChange事件中觸發(fā)的更新是同步優(yōu)先級(最高優(yōu))

          • 用戶可以接受請求發(fā)出到返回之間有等待時間,所以useEffect中觸發(fā)的更新是默認優(yōu)先級


          那么優(yōu)先級怎么表示呢?用一個31位的二進制,被稱為lane

          比如同步優(yōu)先級和默認優(yōu)先級定義如下:

          const SyncLane =    0b0000000000000000000000000000001;
          const DefaultLane = 0b0000000000000000000000000010000;

          數(shù)值越小優(yōu)先級越大,即SyncLane < DefaultLane

          那么React每次更新是不是選擇一個優(yōu)先級,然后執(zhí)行所有組件中這個優(yōu)先級對應(yīng)的更新呢?

          不是。如果每次更新只能選擇一個優(yōu)先級,那靈活性就太差了。

          所以實際情況是:每次更新,React會選擇一到多個lane組成一個批次,然后執(zhí)行所有組件中包含在這個批次中的lane對應(yīng)的更新

          這種組成批次的lane被稱為lanes

          比如,如下代碼將SyncLaneDefaultLane合成lanes

          // 用“按位或”操作合并lane
          const lanes = SyncLane | DefaultLane;

          entangle機制



          可以看到,lane機制本質(zhì)上就是各種位運算,可以設(shè)計的很靈活。


          在此基礎(chǔ)上,有一套被稱為entangle(糾纏)的機制。

          entangle指一種lane之間的關(guān)系,如果laneAlaneB糾纏,那么某次更新React選擇了laneA,則必須帶上laneB

          也就是說laneAlaneB糾纏在一塊,同生共死了。

          除此之外,如果laneAlaneC糾纏,此時laneClaneB糾纏,那么laneA也會與laneB糾纏。

          那么entangle機制與useTransition有什么關(guān)系呢?

          startTransition包裹的回調(diào)中觸發(fā)的更新,優(yōu)先級為TransitionLanes中的一個。

          TransitionLanes中包括16個lane,分別是TransitionLane1TransitionLane16


          transition相關(guān)lane會發(fā)生糾纏。

          在我們的Demo中,每次onChange執(zhí)行,都會創(chuàng)建兩個更新:

          onChange={({target: {value}}) => {
            updateCtn(value);
            startTransition(() => updateNum(num + 1))
          }

          其中:

          • updateCtn(value)由于在onChange中觸發(fā),優(yōu)先級為SyncLane

          • updateNum(num + 1)由于在startTransition中觸發(fā),優(yōu)先級為TransitionLanes中的某一個


          當在輸入框中反復(fù)輸入文字時,以上過程會反復(fù)執(zhí)行,區(qū)別是:

          • SyncLane由于是最高優(yōu)先級,會被執(zhí)行,所以我們會看到輸入框中內(nèi)容變化

          • TransitionLanes相關(guān)lane優(yōu)先級比SyncLane低,暫時不會執(zhí)行,同時他們會產(chǎn)生糾纏


          為了防止某次更新由于優(yōu)先級過低,一直無法執(zhí)行,React有個過期機制:每個更新都有個過期時間,如果在過期時間內(nèi)都沒有執(zhí)行,那么他就會過期。

          過期后的更新會同步執(zhí)行(也就是說他的優(yōu)先級變得和SyncLane一樣)
          在我們的例子中,startTransition(() => updateNum(num + 1))會產(chǎn)生很多糾纏在一塊的TransitionLanes相關(guān)lane

          過了一段時間,其中某個lane過期了,于是他優(yōu)先級提高到和SyncLane一樣,立刻執(zhí)行。

          又由于這個lane與其他TransitionLanes相關(guān)lane糾纏在一起,所以他們會被一起執(zhí)行。

          這就表現(xiàn)為:在輸入框一直輸入內(nèi)容,但是num在視圖中顯示的數(shù)字過了會兒才變化。

          總結(jié)



          今天我們聊了useTransition內(nèi)部的一些實現(xiàn),涉及到:

          • lane模型

          • entangle機制

          • 更新過期機制


          最有意思的是,由于不同電腦性能不同,瀏覽器幀率會變動,所以在不同電腦中React會動態(tài)調(diào)節(jié)防抖的效果。

          這就相當于不需要你手動設(shè)置debounce的時間參數(shù),React會根據(jù)電腦性能動態(tài)調(diào)整。



          點擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開更多互動和交流,掃描下方”二維碼“或在“公眾號后臺回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

          - END -


          瀏覽 30
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  狠狠操夜夜操 | 亚洲第一免费 | 日韩av在线影音先锋 | 国产精品一区二区黑人巨大 | 欧美丰满少妇人妻精品 |