React如何原生實現防抖?
大家好,我卡頌。
作為前端,想必你對防抖( debounce
)、節流( throttle
)這兩個概念不陌生。
在 React18
中,基於新的併發特性, React
原生實現了防抖的功能。
今天我們來聊聊這是如何實現的。
歡迎加入人類高質量前端框架群,帶飛
useTransition Demo
useTransition
是一個新增的原生 Hook
,用於 以較低優先順序執行一些更新 。
在我們的 Demo
中有 ctn
與 num
兩個狀態,其中 ctn
與輸入框的內容受控。
當觸發輸入框 onChange
事件時,會同時觸發 ctn
與 num
狀態變化。其中 觸發num狀態變化的方法 (即 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
迴圈人為增加元件 render
所消耗的時間:
const BusyChild = React.memo(({num}: {num: number}) => { const cur = performance.now(); // 增加render的耗時 while (performance.now() - cur < 300) {} return <div>{num}</div>; })
所以,在輸入框輸入內容時能明顯感到卡頓。
按理說, onChange
中會同時觸發 ctn
與 num
的狀態變化,他們在檢視中的顯示應該是同步的。
然而實際上,輸入框連續輸入一段文字(即 ctn
的狀態變化連續展示在檢視中)後, num
才會變化一次。
如下圖,初始時輸入框沒有內容, num
為0:
輸入框輸入很長一段文字後, num
才變為1:
這種效果就像:被 startTransition
包裹的更新都有 防抖 的效果一樣。
這是如何實現的呢?
什麼是lane
在 React18
中有一套 更新優先順序機制 ,不同地方觸發的更新擁有不同優先順序。優先順序的定義依據是符合使用者感知的,比如:
onChange useEffect
那麼優先順序怎麼表示呢?用一個31位的二進位制,被稱為 lane
。
比如 同步優先順序 和 預設優先順序 定義如下:
const SyncLane = 0b0000000000000000000000000000001; const DefaultLane = 0b0000000000000000000000000010000;
數值越小優先順序越大,即 SyncLane < DefaultLane
。
那麼 React
每次更新是不是選擇一個 優先順序
,然後執行所有元件中 這個優先順序對應的更新 呢?
不是。如果每次更新只能選擇一個 優先順序
,那靈活性就太差了。
所以實際情況是:每次更新, React
會選擇一到多個 lane
組成一個批次,然後執行所有元件中 包含在這個批次中的lane對應的更新
這種組成批次的 lane
被稱為 lanes
。
比如,如下程式碼將 SyncLane
與 DefaultLane
合成 lanes
:
// 用“按位或”操作合併lane const lanes = SyncLane | DefaultLane;
entangle機制
可以看到, lane
機制本質上就是各種位運算,可以設計的很靈活。
在此基礎上,有一套被稱為 entangle
(糾纏)的機制。
entangle
指一種 lane
之間的關係,如果 laneA
與 laneB
糾纏,那麼某次更新 React
選擇了 laneA
,則必須帶上 laneB
。
也就是說 laneA
與 laneB
糾纏在一塊,同生共死了。
除此之外,如果 laneA
與 laneC
糾纏,此時 laneC
與 laneB
糾纏,那麼 laneA
也會與 laneB
糾纏。
那麼 entangle
機制與 useTransition
有什麼關係呢?
被 startTransition
包裹的回撥中觸發的更新,優先順序為 TransitionLanes
中的一個。
TransitionLanes
中包括16個 lane
,分別是 TransitionLane1
到 TransitionLane16
:
而 transition相關lane
會發生糾纏。
在我們的 Demo
中,每次 onChange
執行,都會建立兩個更新:
onChange={({target: {value}}) => { updateCtn(value); startTransition(() => updateNum(num + 1)) }
其中:
-
updateCtn(value)
由於在onChange
中觸發,優先順序為SyncLane
-
updateNum(num + 1)
由於在startTransition
中觸發,優先順序為TransitionLanes
中的某一個
當在輸入框中反覆輸入文字時,以上過程會反覆執行,區別是:
-
SyncLane
由於是最高優先順序,會被執行,所以我們會看到輸入框中內容變化 -
TransitionLanes相關lane
優先順序比SyncLane
低,暫時不會執行,同時他們會產生糾纏
為了防止某次更新由於優先順序過低,一直無法執行, React
有個 過期機制 :每個更新都有個過期時間,如果在過期時間內都沒有執行,那麼他就會過期。
過期後的更新會同步執行(也就是說他的優先順序變得和 SyncLane
一樣)
在我們的例子中, startTransition(() => updateNum(num + 1))
會產生很多糾纏在一塊的 TransitionLanes相關lane
。
過了一段時間,其中某個 lane
過期了,於是他優先順序提高到和 SyncLane
一樣,立刻執行。
又由於這個 lane
與其他 TransitionLanes相關lane
糾纏在一起,所以他們會被一起執行。
這就表現為:在輸入框一直輸入內容,但是 num
在檢視中顯示的數字過了會兒才變化。
總結
今天我們聊了 useTransition
內部的一些實現,涉及到:
lane entangle
最有意思的是,由於不同電腦效能不同,瀏覽器幀率會變動,所以在不同電腦中 React
會動態調節防抖的效果。
這就相當於不需要你手動設定 debounce
的時間引數, React
會根據電腦效能動態調整。
- 為什麼一定要從DevOps走向BizDevOps?
- 雲音樂FeatureStore建設與實踐
- web技術分享| 【高德地圖】實現自定義的軌跡回放
- Object.prototype.toString.call()的原理
- 探針技術-JavaAgent 和位元組碼增強技術-Byte Buddy
- 解決方案| 快對講綜合排程系統
- MAUI模板專案閃退問題
- 2022 年你手機裡有哪些堪稱神器的 App?
- 如何在 React Native 專案中使用 MQTT
- spring-authorization-server令牌放發源碼解析
- 劉勇智:一碼通缺陷分析與架構設計方案丨聲網開發者創業講堂 Vol.02
- systrace 統計方法耗時
- 孫勇男:實時影片 SDK 黑盒測試架構丨Dev for Dev 專欄
- 通俗易懂講解並手寫一個vue資料雙向繫結案例
- 論 T 級互動開發如何在我們手上發光發熱
- 用原生JavaScript寫一個貪吃蛇
- 面試突擊53:常見的 HTTP 狀態碼有哪些?
- 詳解“開放雲”的真正含義!
- 一字一圖,領略瀏覽器方向的優化
- 天才製造者:獨行俠、科技巨頭和AI|深度學習崛起十年