DOM在Ahooks中的處理過程
導讀 | 一個優秀的工具庫應該有自己的一套輸入輸出規範,一來能夠支援更多的場景,二來可以更好的在內部進行封裝處理,三來使用者能夠更加快速熟悉和使用相應的功能,能做到舉一反三。 |
本篇文章探討一下 ahooks 對 DOM 類 Hooks 使用規範,以及原始碼中是如何去做處理的。
這一章節,大部分參考官方文件的 DOM 類 Hooks 使用規範[1]。
第一點,ahooks 大部分 DOM 類 Hooks 都會接收 target 引數,表示要處理的元素。
target 支援三種型別 React.MutableRefObject(透過 useRef 儲存的 DOM)、HTMLElement、() => HTMLElement(一般運用於 SSR 場景)。
第二點,DOM 類 Hooks 的 target 是支援動態變化的。如下所示:
export default () => { const [boolean, { toggle }] = useBoolean(); const ref = useRef(null); const ref2 = useRef(null); const isHovering = useHover(boolean ? ref : ref2); return ( <>{isHovering ? 'hover' : 'leaveHover'}{isHovering ? 'hover' : 'leaveHover'}); };
那 ahooks 是怎麼處理這兩點的呢?
獲取到對應的 DOM 元素,這一點主要相容以上第一點的入參規範。
假如是函式,則取執行完後的結果。
假如擁有 current 屬性,則取 current 屬性的值,相容React.MutableRefObject 型別。
最後就是普通的 DOM 元素。
export function getTargetElement(target: BasicTarget, defaultElement?: T) { // 省略部分程式碼... let targetElement: TargetValue; if (isFunction(target)) { // 支援函式獲取 targetElement = target(); // 假如 ref,則返回 current } else if ('current' in target) { targetElement = target.current; // 支援 DOM } else { targetElement = target; } return targetElement; }
這個方法,主要是為了支援第二點,支援 target 動態變化。
其中 packages/hooks/src/utils/useEffectWithTarget.ts 是使用 useEffect。
import { useEffect } from 'react'; import createEffectWithTarget from './createEffectWithTarget'; const useEffectWithTarget = createEffectWithTarget(useEffect); export default useEffectWithTarget;
另外 其中 packages/hooks/src/utils/useLayoutEffectWithTarget.ts 是使用 useLayoutEffect。
import { useLayoutEffect } from 'react'; import createEffectWithTarget from './createEffectWithTarget'; const useEffectWithTarget = createEffectWithTarget(useLayoutEffect); export default useEffectWithTarget;
兩者都是呼叫的 createEffectWithTarget,只是入參不同。
直接重點看這個 createEffectWithTarget 函式:
createEffectWithTarget 返回的函式useEffectWithTarget接受三個引數,前兩個跟 useEffect 一樣,第三個就是 target。
useEffectType 就是 useEffect 或者 useLayoutEffect。注意這裡呼叫的時候,沒傳第二個引數,也就是每次都會執行。
hasInitRef 判斷是否已經初始化。lastElementRef 記錄的是最後一次 target 元素的列表。lastDepsRef 記錄的是最後一次的依賴。unLoadRef 是執行完 effect 函式(對應的就是 useEffect 中的 effect 函式)的返回值,在元件解除安裝的時候執行。
第一次執行的時候,執行相應的邏輯,並記錄下最後一次執行的相應的 target 元素以及依賴。
後面每次執行的時候,都判斷目標元素或者依賴是否發生變化,發生變化,則執行對應的 effect 函式。並更新最後一次執行的依賴。
元件解除安裝的時候,執行 unLoadRef.current?.() 函式,並重置 hasInitRef 為 false。
const createEffectWithTarget = (useEffectType: typeof useEffect | typeof useLayoutEffect) => { /** * @param effect * @param deps * @param target target should compare ref.current vs ref.current, dom vs dom, ()=>dom vs ()=>dom */ const useEffectWithTarget = ( effect: EffectCallback, deps: DependencyList, target: BasicTarget| BasicTarget[], ) => { const hasInitRef = useRef(false); const lastElementRef = useRef<(Element | null)[]>([]); const lastDepsRef = useRef([]); const unLoadRef = useRef(); // useEffect 或者 useLayoutEffect useEffectType(() => { // 處理 DOM 目標元素 const targets = Array.isArray(target) ? target : [target]; const els = targets.map((item) => getTargetElement(item)); // init run // 首次初始化的時候執行 if (!hasInitRef.current) { hasInitRef.current = true; lastElementRef.current = els; lastDepsRef.current = deps; // 執行回撥中的 effect 函式 unLoadRef.current = effect(); return; } // 非首次執行的邏輯 if ( // 目標元素或者依賴發生變化 els.length !== lastElementRef.current.length || !depsAreSame(els, lastElementRef.current) || !depsAreSame(deps, lastDepsRef.current) ) { // 執行上次返回的結果 unLoadRef.current?.(); // 更新 lastElementRef.current = els; lastDepsRef.current = deps; unLoadRef.current = effect(); } }); useUnmount(() => { // 解除安裝 unLoadRef.current?.(); // for react-refresh hasInitRef.current = false; }); }; return useEffectWithTarget; };
一個優秀的工具庫應該有自己的一套輸入輸出規範,一來能夠支援更多的場景,二來可以更好的在內部進行封裝處理,三來使用者能夠更加快速熟悉和使用相應的功能,能做到舉一反三。
原文來自:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2906855/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- python中PCA的處理過程PythonPCA
- Linux 核心處理中斷全過程解析Linux
- 【解讀 ahooks 原始碼系列】DOM篇(一)Hook原始碼
- 如何處理六西格瑪過程中遇到的問題?
- Flink流處理過程的部分原理分析
- 【原始碼】Redis命令處理過程原始碼Redis
- 揭秘Vue從Virtual DOM生成真實DOM的過程Vue
- 在Spring Bean例項過程中,如何使用反射和遞迴處理的Bean屬性填充?SpringBean反射遞迴
- [MySQL光速入門]017 儲存過程中的"異常處理"MySql儲存過程
- 記一次linux主機中病毒處理過程Linux
- GC析構物件和列表的處理過程GC物件
- 一次壞塊的處理過程(一)
- 一次壞塊的處理過程(二)
- 【Tomcat】Tomat 處理請求的過程(圖解)Tomcat圖解
- MySQL儲存過程的異常處理方法MySql儲存過程
- fastHttp服務端處理請求的過程ASTHTTP服務端
- Ceph pg unfound處理過程詳解
- 大資料的處理是怎樣的過程大資料
- nacos2.3 密碼驗證的處理過程密碼
- Linux伺服器被入侵後的處理過程Linux伺服器
- npm install過程失敗的幾種處理方法NPM
- 記一次PMML檔案的處理過程
- nginx 處理客戶端請求的完整過程Nginx客戶端
- (四)SpringBoot啟動過程的分析-預處理ApplicationContextSpring BootAPPContext
- 大資料處理過程是怎樣大資料
- 處理尚不存在的 DOM 節點
- Vue.js從Virtual DOM對映到真實DOM的過程Vue.js
- DOM0、DOM2、DOM3事件處理方式的區別是什麼?事件
- Nginx圖片下載不完整的處理過程Nginx
- 流處理器與其他部件協同工作的過程
- 實時流處理與分散式儲存過程中對檔案的操作分散式儲存過程
- SpringMVC請求處理過程原始碼簡析SpringMVC原始碼
- Linux Yum 安裝失敗處理過程整理Linux
- zookeeper原始碼 — 五、處理寫請求過程原始碼
- 記一次ceph pg unfound處理過程
- 物件轉json字串的過程中對value為null的值的一些處理物件JSON字串Null
- puppeteer在開發過程中的實踐
- 記一次Nodejs安全工單的處理過程_20171226NodeJS