成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Ahooks 是怎么處理 DOM 的?

開(kāi)發(fā) 前端
一個(gè)優(yōu)秀的工具庫(kù)應(yīng)該有自己的一套輸入輸出規(guī)范,一來(lái)能夠支持更多的場(chǎng)景,二來(lái)可以更好的在內(nèi)部進(jìn)行封裝處理,三來(lái)使用者能夠更加快速熟悉和使用相應(yīng)的功能,能做到舉一反三。

本篇文章探討一下 ahooks 對(duì) DOM 類(lèi) Hooks 使用規(guī)范,以及源碼中是如何去做處理的。

DOM 類(lèi) Hooks 使用規(guī)范

這一章節(jié),大部分參考官方文檔的 DOM 類(lèi) Hooks 使用規(guī)范[1]。

第一點(diǎn),ahooks 大部分 DOM 類(lèi) Hooks 都會(huì)接收 target 參數(shù),表示要處理的元素。

target 支持三種類(lèi)型 React.MutableRefObject(通過(guò) useRef 保存的 DOM)、HTMLElement、() => HTMLElement(一般運(yùn)用于 SSR 場(chǎng)景)。

第二點(diǎn),DOM 類(lèi) Hooks 的 target 是支持動(dòng)態(tài)變化的。如下所示:

export default () => {
const [boolean, { toggle }] = useBoolean();

const ref = useRef(null);
const ref2 = useRef(null);

const isHovering = useHover(boolean ? ref : ref2);
return (
<>
<div ref={ref}>{isHovering ? 'hover' : 'leaveHover'}</div>
<div ref={ref2}>{isHovering ? 'hover' : 'leaveHover'}</div>
</>
);
};

那 ahooks 是怎么處理這兩點(diǎn)的呢?

getTargetElement

獲取到對(duì)應(yīng)的 DOM 元素,這一點(diǎn)主要兼容以上第一點(diǎn)的入?yún)⒁?guī)范。

  • 假如是函數(shù),則取執(zhí)行完后的結(jié)果。
  • 假如擁有 current 屬性,則取 current 屬性的值,兼容React.MutableRefObject 類(lèi)型。
  • 最后就是普通的 DOM 元素。
export function getTargetElement<T extends TargetType>(target: BasicTarget<T>, defaultElement?: T) {
// 省略部分代碼...
let targetElement: TargetValue<T>;

if (isFunction(target)) {
// 支持函數(shù)獲取
targetElement = target();
// 假如 ref,則返回 current
} else if ('current' in target) {
targetElement = target.current;
// 支持 DOM
} else {
targetElement = target;
}

return targetElement;
}

useEffectWithTarget

這個(gè)方法,主要是為了支持第二點(diǎn),支持 target 動(dòng)態(tài)變化。

其中 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;

兩者都是調(diào)用的 createEffectWithTarget,只是入?yún)⒉煌?/p>

直接重點(diǎn)看這個(gè) createEffectWithTarget 函數(shù):

  • createEffectWithTarget 返回的函數(shù)useEffectWithTarget接受三個(gè)參數(shù),前兩個(gè)跟 useEffect 一樣,第三個(gè)就是 target。
  • useEffectType 就是 useEffect 或者 useLayoutEffect。注意這里調(diào)用的時(shí)候,沒(méi)傳第二個(gè)參數(shù),也就是每次都會(huì)執(zhí)行。
  • hasInitRef 判斷是否已經(jīng)初始化。lastElementRef 記錄的是最后一次 target 元素的列表。lastDepsRef 記錄的是最后一次的依賴(lài)。unLoadRef 是執(zhí)行完 effect 函數(shù)(對(duì)應(yīng)的就是 useEffect 中的 effect 函數(shù))的返回值,在組件卸載的時(shí)候執(zhí)行。
  • 第一次執(zhí)行的時(shí)候,執(zhí)行相應(yīng)的邏輯,并記錄下最后一次執(zhí)行的相應(yīng)的 target 元素以及依賴(lài)。
  • 后面每次執(zhí)行的時(shí)候,都判斷目標(biāo)元素或者依賴(lài)是否發(fā)生變化,發(fā)生變化,則執(zhí)行對(duì)應(yīng)的 effect 函數(shù)。并更新最后一次執(zhí)行的依賴(lài)。
  • 組件卸載的時(shí)候,執(zhí)行 unLoadRef.current?.() 函數(shù),并重置 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<any> | BasicTarget<any>[],
) => {
const hasInitRef = useRef(false);

const lastElementRef = useRef<(Element | null)[]>([]);
const lastDepsRef = useRef<DependencyList>([]);

const unLoadRef = useRef<any>();

// useEffect 或者 useLayoutEffect
useEffectType(() => {
// 處理 DOM 目標(biāo)元素
const targets = Array.isArray(target) ? target : [target];
const els = targets.map((item) => getTargetElement(item));

// init run
// 首次初始化的時(shí)候執(zhí)行
if (!hasInitRef.current) {
hasInitRef.current = true;
lastElementRef.current = els;
lastDepsRef.current = deps;
// 執(zhí)行回調(diào)中的 effect 函數(shù)
unLoadRef.current = effect();
return;
}
// 非首次執(zhí)行的邏輯
if (
// 目標(biāo)元素或者依賴(lài)發(fā)生變化
els.length !== lastElementRef.current.length ||
!depsAreSame(els, lastElementRef.current) ||
!depsAreSame(deps, lastDepsRef.current)
) {
// 執(zhí)行上次返回的結(jié)果
unLoadRef.current?.();

// 更新
lastElementRef.current = els;
lastDepsRef.current = deps;
unLoadRef.current = effect();
}
});

useUnmount(() => {
// 卸載
unLoadRef.current?.();
// for react-refresh
hasInitRef.current = false;
});
};

return useEffectWithTarget;
};

思考與總結(jié)

一個(gè)優(yōu)秀的工具庫(kù)應(yīng)該有自己的一套輸入輸出規(guī)范,一來(lái)能夠支持更多的場(chǎng)景,二來(lái)可以更好的在內(nèi)部進(jìn)行封裝處理,三來(lái)使用者能夠更加快速熟悉和使用相應(yīng)的功能,能做到舉一反三。

責(zé)任編輯:武曉燕 來(lái)源: 前端雜貨鋪
相關(guān)推薦

2022-06-08 08:01:20

useEffect數(shù)組函數(shù)

2022-06-09 09:20:41

ahooksaxios

2022-06-10 08:01:01

ahookshook代碼

2023-06-13 07:54:17

DOM 封裝作用域

2014-05-26 16:16:59

Shadow DomWeb Compone

2023-02-26 08:42:10

源碼demouseEffect

2009-02-10 09:23:03

DOM模型MSXML

2024-10-14 12:15:11

2022-06-28 07:41:38

useMountReactahooks

2022-08-16 21:01:56

runAsyncreload數(shù)據(jù)

2021-01-21 09:09:18

時(shí)區(qū)轉(zhuǎn)換程序

2021-12-08 07:49:46

Ahooks 3.0React Hooks

2023-09-25 10:26:05

DOMCSS

2020-11-11 14:19:17

隱私APP設(shè)計(jì)

2022-06-06 08:02:21

ahooks架構(gòu)hooks

2009-02-27 17:15:05

XMLDOMXPath

2010-09-09 17:19:07

HTML DOMXML DOM

2010-09-28 10:24:50

HTML DOMXML DOM

2021-08-06 11:24:35

域名劫持網(wǎng)站安全網(wǎng)絡(luò)攻擊

2017-09-04 18:48:14

TomcatSpringBoot容器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 黄色片在线网站 | 九九热精品视频 | 成人午夜av| 欧美a级成人淫片免费看 | 综合一区二区三区 | 亚洲精品视频观看 | 日本黄色一级片视频 | 中文字幕亚洲精品 | 成人国产免费观看 | 日韩中文字幕视频在线 | 国精日本亚洲欧州国产中文久久 | 欧美日韩亚洲国产 | 欧美午夜一区二区三区免费大片 | 亚洲精品综合 | 亚av在线 | 亚洲在线一区二区三区 | 国产特黄一级 | 小早川怜子xxxxaⅴ在线 | 欧美男人天堂 | 成人不卡在线 | 精品在线观看一区二区 | 午夜网站视频 | 亚洲精品福利在线 | 国产欧美性成人精品午夜 | 91xxx在线观看| 99re99| 男人天堂网址 | 亚洲国产成人精品久久久国产成人一区 | 国产在线观看一区二区 | 中文字幕三区 | 欧美日韩在线视频一区 | 亚洲精品在线观看视频 | 天天操网 | 亚洲欧洲激情 | 影音av| 黄色一级毛片 | 日本免费视频在线观看 | 欧美一区在线视频 | 精品视频一区二区三区 | 男女黄网站 | 日本久久综合 |