1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        React如何原生實(shí)現(xiàn)防抖?

        共 3859字,需瀏覽 8分鐘

         ·

        2022-05-20 21:04

        作者:卡頌

        簡(jiǎn)介:《React技術(shù)揭秘》作者

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


        大家好,我卡頌。


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


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


        今天我們來(lái)聊聊這是如何實(shí)現(xiàn)的。


        useTransition Demo



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


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


        當(dāng)觸發(fā)輸入框onChange事件時(shí),會(huì)同時(shí)觸發(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會(huì)作為props傳遞給BusyChild組件。在BusyChild中通過(guò)while循環(huán)人為增加組件render所消耗的時(shí)間:


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

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


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



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


        按理說(shuō),onChange中會(huì)同時(shí)觸發(fā)ctnnum的狀態(tài)變化,他們?cè)谝晥D中的顯示應(yīng)該是同步的。


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


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



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



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


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


        什么是lane



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


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

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


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

        比如同步優(yōu)先級(jí)和默認(rèn)優(yōu)先級(jí)定義如下:

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

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

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

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

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

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

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

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

        entangle機(jī)制



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


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

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

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

        除此之外,如果laneAlaneC糾纏,此時(shí)laneClaneB糾纏,那么laneA也會(huì)與laneB糾纏。

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

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

        TransitionLanes中包括16個(gè)lane,分別是TransitionLane1TransitionLane16


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

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

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

        其中:

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

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


        當(dāng)在輸入框中反復(fù)輸入文字時(shí),以上過(guò)程會(huì)反復(fù)執(zhí)行,區(qū)別是:

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

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


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

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

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

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

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

        總結(jié)



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

        • lane模型

        • entangle機(jī)制

        • 更新過(guò)期機(jī)制


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

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



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

        - END -


        瀏覽 30
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            偷拍美女洗澡网站 | 俄罗斯美女毛片 | 99久热re在线精品视频大全 | 又粗又孟又色又爽视频在线观看 | 善交bbw搡bbbb搡bbbb | 大屌操大逼 | 女被男c到爽哭视频免费网站 | 国产成人无码视频在线观看 | 毛j片Aaaa婬片一级 | 美女被草的网站 |