resize-observer源码解读

resize-observer

github 地址:https://github.com/devrelm/resize-observer

本地启动

npm install

npm start

node 18.16.0 (npm 9.5.1) 启动失败报错

node:internal/crypto/hash:71
  this[kHandle] = new _Hash(algorithm, xofLen);
                  ^

Error: error:0308010C:digital envelope routines::unsupported

解决:更改 node 版本

node 16.16.0 (npm 8.11.0) 启动成功

在这里插入图片描述

使用示例

const onResize: ResizeObserverProps["onResize"] = ({
  width,
  height,
  offsetHeight,
  offsetWidth,
}) => {
  setTimes((prevTimes) => prevTimes + 1);
  console.log(
    "Resize:",
    "\n",
    "BoundingBox",
    width,
    height,
    "\n",
    "Offset",
    offsetWidth,
    offsetHeight
  );
};

<ResizeObserver onResize={onResize} disabled={disabled}>
  <Wrapper>
    <textarea ref={textareaRef} placeholder="I'm a textarea!" />
  </Wrapper>
</ResizeObserver>;
export type OnResize = (size: SizeInfo, element: HTMLElement) => void;

export interface ResizeObserverProps {
  /** Pass to ResizeObserver.Collection with additional data */
  data?: any;
  children:
    | React.ReactNode
    | ((ref: React.RefObject<any>) => React.ReactElement);
  disabled?: boolean;
  /** Trigger if element resized. Will always trigger when first time render. */
  onResize?: OnResize;
}

ResizeObserve 組件

真正组件在ResizeObserver组件

const RefResizeObserver = React.forwardRef(ResizeObserver) as React.ForwardRefExoticComponent<
  React.PropsWithoutRef<ResizeObserverProps> & React.RefAttributes<any>
> & {
  Collection: typeof Collection;
};

RefResizeObserver.Collection = Collection;

export default RefResizeObserver;

ResizeObserver

里面还有一层组件SingleObserver

//src\index.tsx
function ResizeObserver(props: ResizeObserverProps, ref: React.Ref<HTMLElement>) {

  return childNodes.map((child, index) => {
    const key = child?.key || `${INTERNAL_PREFIX_KEY}-${index}`;
    return (
      <SingleObserver {...props} key={key} ref={index === 0 ? ref : undefined}>
        {child}
      </SingleObserver>
    );
  }) as any as React.ReactElement;
}


SingleObserver 組件

真正实现观察的方法在这个组件

const RefSingleObserver = React.forwardRef(SingleObserver);
//src\SingleObserver\index.tsx
function SingleObserver(
  props: SingleObserverProps,
  ref: React.Ref<HTMLElement>
) {
  return (
    <DomWrapper ref={wrapperRef}>
      {canRef
        ? React.cloneElement(mergedChildren as any, {
            ref: mergedRef,
          })
        : mergedChildren}
    </DomWrapper>
  );
}

实现元素变化逻辑

监听 elementRef.current 的變化
在 SingleObserver 组件

import { observe, unobserve } from "../utils/observerUtil";

// Dynamic observe
React.useEffect(() => {
  // getDom获取要被侦听的element
  const currentElement: HTMLElement = getDom();

  if (currentElement && !disabled) {
    // 执行侦听
    observe(currentElement, onInternalResize);
  }
  // 清除侦听
  return () => unobserve(currentElement, onInternalResize);
}, [elementRef.current, disabled]);

创建侦听器实例

// src\utils\observerUtil.ts
const elementListeners = new Map<Element, Set<ResizeListener>>();
import ResizeObserver from 'resize-observer-polyfill';

// interface ResizeObserverEntry {
//     readonly target: Element;
//     readonly contentRect: DOMRectReadOnly;
// }

// onResize 创建侦听器传入的callback
function onResize(entities: ResizeObserverEntry[]) {
  entities.forEach((entity) => {
    const { target } = entity;
    // elementListeners.get(target)是set集合 ,listener是回调函数onInternalResize
    elementListeners.get(target)?.forEach((listener) => listener(target));
  });
}

// Note: ResizeObserver polyfill not support option to measure border-box resize
const resizeObserver = new ResizeObserver(onResize);
// resize-observer-polyfill中ResizeObserverSPI类
const observer = new ResizeObserverSPI(callback, controller, this);

ResizeObserverSPI类的broadcastActive方法
callback返回的信息,entries是一个数组,返回所有正在活跃的目标element列表

// Create ResizeObserverEntry instance for every active observation.
const entries = this.activeObservations_.map((observation) => {
  // 返回被觀察element最新的大小
  return new ResizeObserverEntry(
    observation.target,
    // 執行observation.broadcastRect函數獲取最新的大小
    observation.broadcastRect()
  );
});

// 改变回调函数的this指向ctx
this.callback_.call(ctx, entries, ctx);

observe 函数

const elementListeners = new Map<Element, Set<ResizeListener>>();


function observe(element: Element, callback: ResizeListener) {
  if (!elementListeners.has(element)) {
    // 给elementListeners添加一个键值对
    elementListeners.set(element, new Set());
    //
    resizeObserver.observe(element);
  }
// elementListeners.get(element) 是set结构,给set插入一个新元素callback回调函数即onInternalResize
  elementListeners.get(element).add(callback);
}

unobserve 函数

const elementListeners = new Map<Element, Set<ResizeListener>>();

// 取消侦听
function unobserve(element: Element, callback: ResizeListener) {
  if (elementListeners.has(element)) {
    //set集合移除callback回调函数
    elementListeners.get(element).delete(callback);

    if (!elementListeners.get(element).size) {
      // 取消侦听
      resizeObserver.unobserve(element);
      // 移除目标element
      elementListeners.delete(element);
    }
  }
}

onInternalResize 函数

CollectionContext = React.createContext<onCollectionResize>(null);
const onCollectionResize = React.useContext(CollectionContext);


const propsRef = React.useRef < SingleObserverProps > props;
propsRef.current = props;

// Handler
const onInternalResize = React.useCallback((target: HTMLElement) => {
  const { onResize, data } = propsRef.current;

  // getBoundingClientRect侦听器内部实现的一个方法,获取元素尺寸大小
  const { width, height } = target.getBoundingClientRect();
  const { offsetWidth, offsetHeight } = target;

  /**
   * Resize observer trigger when content size changed.
   * In most case we just care about element size,
   * let's use `boundary` instead of `contentRect` here to avoid shaking.
   */
  const fixedWidth = Math.floor(width);
  const fixedHeight = Math.floor(height);

  if (
    sizeRef.current.width !== fixedWidth ||
    sizeRef.current.height !== fixedHeight ||
    sizeRef.current.offsetWidth !== offsetWidth ||
    sizeRef.current.offsetHeight !== offsetHeight
  ) {
    const size = {
      width: fixedWidth,
      height: fixedHeight,
      offsetWidth,
      offsetHeight,
    };
    sizeRef.current = size;

    // IE is strange, right?
    const mergedOffsetWidth =
      offsetWidth === Math.round(width) ? width : offsetWidth;
    const mergedOffsetHeight =
      offsetHeight === Math.round(height) ? height : offsetHeight;

    const sizeInfo = {
      ...size,
      offsetWidth: mergedOffsetWidth,
      offsetHeight: mergedOffsetHeight,
    };

    // Let collection know what happened
    onCollectionResize?.(sizeInfo, target, data);

    if (onResize) {
      // defer the callback but not defer to next frame
      Promise.resolve().then(() => {
        // 给父组件传递信息
        onResize(sizeInfo, target);
      });
    }
  }
}, []);



getDom 函數

const getDom = () =>
  findDOMNode<HTMLElement>(elementRef.current) ||
  // Support `nativeElement` format
  (elementRef.current && typeof elementRef.current === 'object'
    ? findDOMNode<HTMLElement>((elementRef.current as any)?.nativeElement)
    : null) ||
  findDOMNode<HTMLElement>(wrapperRef.current);

findDOMNode函數

github:https://github.com/react-component/util/blob/master/src/Dom/findDOMNode.ts

/**
 * Return if a node is a DOM node. Else will return by `findDOMNode`
 */
function findDOMNode<T = Element | Text>(
  node: React.ReactInstance | HTMLElement | SVGElement,
): T {
  if (isDOM(node)) {
    return (node as unknown) as T;
  }

  if (node instanceof React.Component) {
    return (ReactDOM.findDOMNode(node) as unknown) as T;
  }

  return null;
}
function isDOM(node: any): node is HTMLElement | SVGElement {
  // https://developer.mozilla.org/en-US/docs/Web/API/Element
  // Since XULElement is also subclass of Element, we only need HTMLElement and SVGElement
  return node instanceof HTMLElement || node instanceof SVGElement;
}

Collection组件

function Collection({ children, onBatchResize }: CollectionProps) {
  const resizeIdRef = React.useRef(0);
  const resizeInfosRef = React.useRef<ResizeInfo[]>([]);

  const onCollectionResize = React.useContext(CollectionContext);

  const onResize = React.useCallback<onCollectionResize>(
    (size, element, data) => {
      resizeIdRef.current += 1;
      const currentId = resizeIdRef.current;

      resizeInfosRef.current.push({
        size,
        element,
        data,
      });

      Promise.resolve().then(() => {
        if (currentId === resizeIdRef.current) {
          onBatchResize?.(resizeInfosRef.current);
          resizeInfosRef.current = [];
        }
      });

      // Continue bubbling if parent exist
      onCollectionResize?.(size, element, data);
    },
    [onBatchResize, onCollectionResize],
  );

  return <CollectionContext.Provider value={onResize}>{children}</CollectionContext.Provider>;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/470627.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

1、初识JVM

一、JVM是什么&#xff1f; JVM的英文全称是 Java Virtual Machine&#xff0c;其中文译名为Java虚拟机。它在本质上就是是一个运行在计算机上的程序&#xff0c;他的职责是运行Java字节码文件。 JVM执行流程如下 二、JVM有哪些功能&#xff1f; 2.1 解释和运行 对字节码文…

【Web技术应用基础】HTML(1)——简单界面

题目1&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title>Hello world</title></head> <body bgcolor"F6F3D6"><!--用HTML语言向世界打声招呼吧&#xff01;--><h1 align&…

电脑怎么快速重装系统win7

电脑重装系统是解决软件问题、提升系统性能的常用手段。随着技术发展,一键重装系统成为了许多用户的首选方法,因为它简化了繁琐的操作步骤,节省了大量时间。尤其是对于非技术人员来说,一键重装提供了一种快速高效且不易出错的系统安装方式。如果你需要快速重装win7,那么可…

Spring Boot 自动化单元测试类的编写过程

前言 Web环境模拟测试 企业开发不仅要保障业务层与数据层的功能安全有效&#xff0c;也要保障表现层的功能正常。但是我们一般对表现层的测试都是通过postman手工测试的&#xff0c;并没有在打包过程中代码体现表现层功能被测试通过。那么能否在测试用例中对表现层进行功能测…

Android 项目实战,APP开发,含源码

Android 项目实战&#xff0c;APP开发&#xff0c;含源码 源码项目详情 源码项目详情 切鱼达人&#xff0c;Android休闲游戏开发 打砖块&#xff0c;Android休闲小游戏开发 “牛弹琴”&#xff0c;Android 弹钢琴 app 开发 2048 数字合成大作战&#xff0c;Android小游戏开…

NCV4276BDT50RKG低压差稳压器芯片中文资料PDF数据手册规格书引脚图参数价格

产品概述&#xff1a; NCV4276B是一款输出电流400 mA集成式低压差稳压器系列&#xff0c;设计用于恶劣的汽车环境。它包括宽工作温度和输入电压范围。该器件提供固定和可调电压版本&#xff0c;输出电压精度为 2%。它具有高峰值输入电压容差和反向输入电压保护。它还提供过流保…

【研发管理】产品经理-基础认知

导读&#xff1a;产品经理&#xff08;Product Manager&#xff09;是一个负责产品的全周期管理的职位&#xff0c;他们不仅参与产品的设计、开发、推广和销售&#xff0c;还涉及到产品的市场调研、用户需求分析、竞争分析、产品规划、产品测试以及后续的产品迭代等多个环节。产…

使用Redis做缓存的小案例

如果不了解Redis&#xff0c;可以查看本人博客&#xff1a;Redis入门 Redis基于内存&#xff0c;因此查询速度快&#xff0c;常常可以用来作为缓存使用&#xff0c;缓存就是我们在内存中开辟一段区域来存储我们查询比较频繁的数据&#xff0c;这样&#xff0c;我们在下一次查询…

Hive 数据迁移与备份

迁移类型 同时迁移表及其数据&#xff08;使用import和export&#xff09; 迁移步骤 将表和数据从 Hive 导出到 HDFS将表和数据从 HDFS 导出到本地服务器将表和数据从本地服务器复制到目标服务器将表和数据从目标服务器上传到目标 HDFS将表和数据从目标 HDFS 上传到目标 Hiv…

设计模式学习笔记 - 设计模式与范式 - 创建型:1.单例模式(上):为什么说支持懒加载的双重校验不必饿汉式更优?

今天开始正式学习设计模式。经典的设计模式有 23 种。其中&#xff0c;常用的并不是很多&#xff0c;可能一半都不到。作为程序员&#xff0c;最熟悉的设计模式&#xff0c;肯定包含单例模式。 本次单例模式的讲解&#xff0c;希望你搞清楚下面这样几个问题。&#xff08;第一…

Redis一些命令(2)

启动命令&#xff1a; redis-server /myredis/redis.conf&#xff08;指定配置文件&#xff09; redis-cli -a 123456 -p 6379&#xff08;-a 密码 -p 端口号&#xff09; redis-cli -a 123456 --raw&#xff08;解决中文乱码&#xff09; 关闭命令&#xff1a; redis-cli…

万用表革新升级,WT588F02BP-14S语音芯片助力智能测量新体验v

万能表功能&#xff1a; 万能表是一款集多功能于一体的电子测量工具&#xff0c;能够精准测量电压、电流、电阻等参数&#xff0c;广泛应用于电气、电子、通信等领域。其操作简便、测量准确&#xff0c;是工程师们进行电路调试、故障排查的得力助手&#xff0c;为提升工作效率…

Go语言学习11-测试

Go语言学习11-测试 单元测试 // functions.go package testingfunc square(op int) int {return op * op }// functions_test.go package testingimport ("fmt""github.com/stretchr/testify/assert""testing" )func TestSquare(t *testing.T)…

Panasonic松下PLC如何数据采集?如何实现快速接入IIOT云平台?

在工业自动化领域&#xff0c;数据采集与远程控制是提升生产效率、优化资源配置的关键环节。对于使用Panasonic松下PLC的用户来说&#xff0c;如何实现高效、稳定的数据采集&#xff0c;并快速接入IIOT云平台&#xff0c;是摆在他们面前的重要课题。HiWoo Box工业物联网关以其强…

Git小乌龟安装及使用教程

一、Win7安装git 软件下载地址&#xff1a;git for windows 安装过程直接默认下一步&#xff0c;直到安装结束。 安装结束后重启一下。 安装完成后&#xff0c;在文件夹空白处右键出现以下几个标识&#xff0c;说明安装成功。 二、安装tortoise git&#xff08;乌龟git&…

鸿蒙Harmony应用开发—ArkTS声明式开发(画布组件:ImageBitmap)

ImageBitmap对象可以存储canvas渲染的像素数据。 说明&#xff1a; 从 API Version 8 开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 接口 ImageBitmap(src: string) 从API version 9开始&#xff0c;该接口支持在ArkTS卡片中使用。 参…

游戏反云手机检测方案

游戏风险环境&#xff0c;是指独立于原有设备或破坏设备原有系统的环境。常见的游戏风险环境有&#xff1a;云手机、虚拟机、虚拟框架、iOS越狱、安卓设备root等。 这类风险环境可以为游戏外挂、破解提供所需的高级别设备权限&#xff0c;当游戏处于这些风险环境下&#xff0c…

Python之Web开发中级教程----ubuntu安装MySQL

Python之Web开发中级教程----ubuntu安装MySQL 进入/opt目录 cd /opt 更新软件源 sudo apt-get upgrade sudo apt-get update 3、安装Mysql server sudo apt-get install mysql-server 4、启动Mysql service mysql start 5、确认Mysql的状态 service mysql status 6、安全设…

springboot286入校申报审批系统的设计与实现

入校申报审批系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装入校申报审批系统软件来发挥其…

课设系统篇

《古代六扇门人员管理系统》 数据库 sixdoor 编码 utf8mb4 视图 查询官员等级 存储过程 CREATE DEFINERrootlocalhost PROCEDURE levelname(IN g_name VARCHAR(20)) BEGINSELECT name,level FROM servingofficials INNER JOIN jobtitle onservingofficials.role jobtitl…
最新文章