我的编程空间,编程开发者的网络收藏夹
学习永远不晚

JavaScript中ahooks 处理 DOM 的方法

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

JavaScript中ahooks 处理 DOM 的方法

前言

目标主要有以下几点:

  • 加深对 React hooks 的理解。
  • 学习如何抽象自定义 hooks。构建属于自己的 React hooks 工具库。
  • 培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择。

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

DOM 类 Hooks 使用规范

这一章节,大部分参考官方文档的 DOM 类 Hooks 使用规范。

第一点,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 (
    <>
      <div ref={ref}>{isHovering ? 'hover' : 'leaveHover'}</div>
      <div ref={ref2}>{isHovering ? 'hover' : 'leaveHover'}</div>
    </>
  );
};

那 ahooks 是怎么处理这两点的呢?

getTargetElement

获取到对应的 DOM 元素,这一点主要兼容以上第一点的入参规范。

  • 假如是函数,则取执行完后的结果。
  • 假如拥有 current 属性,则取 current 属性的值,兼容 React.MutableRefObject 类型。
  • 最后就是普通的 DOM 元素。
export function getTargetElement<T extends TargetType>(target: BasicTarget<T>, defaultElement?: T) {
  // 省略部分代码...
  let targetElement: TargetValue<T>;
  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/class="lazy" data-src/utils/useEffectWithTarget.ts 是使用 useEffect。

import { useEffect } from 'react';
import createEffectWithTarget from './createEffectWithTarget';
const useEffectWithTarget = createEffectWithTarget(useEffect);
export default useEffectWithTarget;

另外 其中 packages/hooks/class="lazy" data-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) => {
  
  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 目标元素
      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;
};

思考与总结

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

到此这篇关于JavaScript中ahooks 处理 DOM 的方法的文章就介绍到这了,更多相关ahooks 处理 DOM内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

JavaScript中ahooks 处理 DOM 的方法

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

javascript中有哪些dom方法

这篇文章给大家分享的是有关javascript中有哪些dom方法的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。javascript是一种什么语言javascript是一种动态类型、弱类型的语言,基于对象和事件驱动
2023-06-14

JavaScript中的异步处理方法

本篇内容介绍了“JavaScript中的异步处理方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在 JavaScript 的世界中,所有代
2023-06-17

JavaScript中Location.search处理使用方法

本文主要介绍了JavaScript中Location.search处理使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-17

javascript全局错误的处理方法

这篇文章主要介绍了javascript全局错误的处理方法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。JavaScript是什么JavaScript是一种直译式的脚本语言,其
2023-06-14

怎么在JavaScript中使用remove方法删除dom节点

今天就跟大家聊聊有关怎么在JavaScript中使用remove方法删除dom节点,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。javascript是一种什么语言javascript
2023-06-14

jquery可以调用dom中的方法

随着Web技术的飞速发展,JavaScript已经成为了Web应用的必备语言之一。而jQuery作为最流行的JavaScript库之一,为开发者提供了一套简明易懂、功能强大的操作DOM的API。在jQuery中,不仅可以直接操作DOM元素的属性和样式,还可以调用DOM中的方法,让开发者更加方便快捷地开发Web应用。在jQuery中,我们可以使用常见的DOM方法,如addClas
2023-05-18

react中的DOM操作的实现方法

本篇内容介绍了“react中的DOM操作的实现方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目录前面的话使用场景ref【HTML元素】【
2023-06-20

编程热搜

目录