良许Linux教程网 干货合集 分享一下DOM在Ahooks中的处理过程

分享一下DOM在Ahooks中的处理过程

一个优秀的工具库应该有自己的一套输入输出规范,一来能够支持更多的场景,二来可以更好的在内部进行封装处理,三来使用者能够更加快速熟悉和使用相应的功能,能做到举一反三。

本篇文章探讨一下 ahooks 对 DOM 类 Hooks 使用规范,以及源码中是如何去做处理的。

DOM在Ahooks中的处理过程DOM在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 是怎么处理这两点的呢?

getTargetElement

获取到对应的 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;
}

useEffectWithTarget

这个方法,主要是为了支持第二点,支持 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([]);
   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;
};

思考与总结

一个优秀的工具库应该有自己的一套输入输出规范,一来能够支持更多的场景,二来可以更好的在内部进行封装处理,三来使用者能够更加快速熟悉和使用相应的功能,能做到举一反三。

以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !

137e00002230ad9f26e78-265x300
本文由 良许Linux教程网 发布,可自由转载、引用,但需署名作者且注明文章出处。如转载至微信公众号,请在文末添加作者公众号二维码。
良许

作者: 良许

良许,世界500强企业Linux开发工程师,公众号【良许Linux】的作者,全网拥有超30W粉丝。个人标签:创业者,CSDN学院讲师,副业达人,流量玩家,摄影爱好者。
上一篇
下一篇

发表评论

联系我们

联系我们

公众号:良许Linux

在线咨询: QQ交谈

邮箱: yychuyu@163.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部