1.原生方法

通过addEventListener添加事件监听窗口的resize事件,触发相应函数从而获取目标元素大小。

//index.less
.box{
	width:100%;
	height:50%;
	display:flex;
	padding:20px
	flex-direction: column;
	.resizebox{
		width:100%;
		flex:1;
	}
}
//index.js
const MyApp = ()=>{
    const resizeRef = useRef<HTMLDivElement>(null);
    const resizeChange = () => {
	   let width= resizeRef. current.offsetWidth
	   let height=resizeRef .current.offsetHeight;
	   console.log(width,height,"======")
	};
    useEffect(() => {
	    // 监听
	    window.addEventListener('resize', resizeChange);
	    // 销毁
	    return () => window.removeEventListener('resize', resizeChange);
	}, []);
	return (
		<div className="box">
			<div className="resizebox"  ref={ resizeRef  }></div>
        </div>
	)
}

2.resizeObserver方法

通过构造一个 ResizeObserver 对象(全局的一个api)以观察者模式监听任意 Element / SvgElement 的尺寸变化。

//index.js
const MyApp = ()=>{
    const resizeRef = useRef<HTMLDivElement>(null);
    const resizeChange = (entry) => {
       const {width,height} =entry.contentRect
	   let width= resizeRef. current.offsetWidth
	   let height=resizeRef .current.offsetHeight;
	   console.log(width,height,"======")
	};
    useEffect(() => {
       const resizeObserver = new ResizeObserver((entries) => {
			if (!Array.isArray(entries) || !entries.length) {
	        return;
	      }
	      for (let entry of entries) {
	        resizeChange (entry));
	        }
	      }
		});
        resizeObserver.observe(resizeRef. current)
        //一定要卸载,不卸载这个,组件销毁后会一直找这个dom元素,会假死也不报错!
        return (): void => { resizeObserver.unobserve(resizeRef. current) };
    }, []);
	return (
		<div className="box">
			<div className="resizebox"  ref={ resizeRef  }></div>
        </div>
	)
}

这样写很麻烦,而且resizeObserver是一个全局的,这里卸载的话,如果其他地方用到就也失效了,应该将不需要监听的元素从resizeObserver的队列中去除,所以为了方便可以全局封装一个resizeObserver,如下:

const _global = window as any;

type ResizeConfig = [Element, ((...args: any) => void)[]];

function createResizeObserver(dom: Element, resizeFn: (...args: any) => void) {
  if (!window.ResizeObserver) {
    return {};
  }
  let resizeObserver: ResizeObserver;
  let resizeObserverConfig: ResizeConfig[];
  let isObserver = false;
  if (_global._RESIZE_OBSERVER_CONFIG) {
    resizeObserverConfig = _global._RESIZE_OBSERVER_CONFIG as ResizeConfig[];
    const index = resizeObserverConfig.findIndex((v) => v[0] === dom);
    if (index > -1) {
      isObserver = true;
      resizeObserverConfig[index][1].push(resizeFn);
    } else {
      resizeObserverConfig.push([dom, [resizeFn]]);
    }
  } else {
    resizeObserverConfig = [];
    resizeObserverConfig.push([dom, [resizeFn]]);
  }
  _global._RESIZE_OBSERVER_CONFIG = resizeObserverConfig;

  if (_global._RESIZE_OBSERVER) {
    resizeObserver = _global._RESIZE_OBSERVER;
  } else {
    resizeObserver = new ResizeObserver((entries) => {
      if (!Array.isArray(entries) || !entries.length) {
        return;
      }
      for (let entry of entries) {
        const resizeItem = resizeObserverConfig.find((v) => v[0] === entry.target);
        const resizeHandles = resizeItem ? resizeItem[1] : undefined;
        if (resizeHandles) {
          resizeHandles.forEach((handle) => handle(entry));
        }
      }
    });
  }

  _global._RESIZE_OBSERVER = resizeObserver;

  if (!isObserver) {
    resizeObserver.observe(dom);
  }
  return resizeObserver;
}

function unResizeObserver(dom: Element, handle: (...args: any) => void) {
  if (!window.ResizeObserver) {
    return;
  }
  let resizeObserverConfig: ResizeConfig[];
  let resizeObserver: ResizeObserver;
  if (_global._RESIZE_OBSERVER_CONFIG) {
    resizeObserverConfig = _global._RESIZE_OBSERVER_CONFIG;
  }
  if (_global._RESIZE_OBSERVER) {
    resizeObserver = _global._RESIZE_OBSERVER;
  }
  if (resizeObserver && resizeObserverConfig) {
    if (handle) {
      const index = resizeObserverConfig.findIndex((v) => v[0] === dom);
      resizeObserverConfig[index][1] = resizeObserverConfig[index][1].filter((v) => v !== handle);
      if (resizeObserverConfig[index][1].length === 0) {
        resizeObserver.unobserve(dom);
        resizeObserverConfig = resizeObserverConfig.filter((v) => v[0] !== dom);
      }
    } else {
      resizeObserverConfig = resizeObserverConfig.filter((v) => v[0] !== dom);
      resizeObserver.unobserve(dom);
    }
    _global._RESIZE_OBSERVER_CONFIG = resizeObserverConfig;
  }
}

const resizeObserver = {
  createResizeObserver,
  unResizeObserver,
};

export default resizeObserver;

使用:

const MyApp = ()=>{
    const resizeRef = useRef<HTMLDivElement>(null);
    const resizeChange = (entry) => {
       const {width,height} =entry.contentRect
	   let width= resizeRef. current.offsetWidth
	   let height=resizeRef .current.offsetHeight;
	   console.log(width,height,"======")
	};
   useLayoutEffect(() => {
    resizeObserver.createResizeObserver(containerRef.current as HTMLElement, resizeChange )
    return () => resizeObserver.unResizeObserver(containerRef.current as HTMLElement, resizeChange )

  }, [resizeChange ]);
	return (
		<div className="box">
			<div className="resizebox"  ref={ resizeRef  }></div>
        </div>
	)
}

3.在 dom元素中添加一个对象标签,监听resize方法

封装函数

class ResizeHelper {
  ele: HTMLDivElement;
  resizeObject: HTMLObjectElement;
  resizeWindow: Window;
  constructor(ele: HTMLDivElement) {
    this.ele = ele;
    this.ele.style.position = 'relative';
    this.resizeObject = this.createObject();
    this.resizeWindow = this.resizeObject.contentDocument.defaultView;
  }
  createObject() {
    const obj = document.createElement('object');
    obj.setAttribute('style', 'display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden;opacity: 0; pointer-events: none; z-index: -1;');
    obj.type = 'text/html';
    obj.data = 'about:blank';
    this.ele.appendChild(obj);
    return obj;
  }
  onResize(fn: (...args: any) => void) {
    this.resizeWindow.addEventListener('resize', fn);
  }
  offResize(fn: (...args: any) => void) {
    try {
      this.resizeWindow.removeEventListener('resize', fn);
    } catch (e) {
      console.warn(e);
    }
  }
  dispose() {
    try {
      this.resizeObject.remove();
      this.resizeWindow = null;
      this.ele = null;
      this.resizeObject = null;
    } catch (e) {
      console.warn(e);
    }
  }
}

export default ResizeHelper;

使用 :

//index.js
const MyApp = ()=>{
    const resizeRef = useRef<HTMLDivElement>(null);
    const resizeChange = (e: any) => {
	   let width= resizeRef. current.offsetWidth
	   let height=resizeRef .current.offsetHeight;
	   console.log(width,height,"======")
	};
    useEffect(() => {
    	const  resizedom = new ResizeHelper (resizeRef )
	    // 监听
	   resizedom .onResize('resize', resizeChange);
	    // 销毁
	    return () => resizedom.dispose();
	}, []);
	return (
		<div className="box">
			<div className="resizebox"  ref={ resizeRef  }></div>
        </div>
	)
}

4.ahooks中useSize

监听 DOM 节点尺寸变化的 Hook。

//index.js
const MyApp = ()=>{
    const resizeRef = useRef<HTMLDivElement>(null);
   const size = useSize(ref);
		console.log(size?.width, size?.height,"=====")
	}, [size ]);
	return (
		<div className="box">
			<div className="resizebox"  ref={ resizeRef  }></div>
        </div>
	)
}

源码其实用的是resize-observer-polyfill中封装好的resizeObserver

import ResizeObserver from 'resize-observer-polyfill';
const resizeObserver = new ResizeObserver((entries) => {
        entries.forEach((entry) => {
          const { clientWidth, clientHeight } = entry.target;
          setState({
            width: clientWidth,
            height: clientHeight,
          });
        });
      });

      resizeObserver.observe(el);
      return () => {
        resizeObserver.disconnect();
      };

resize-observer-polyfill的源码解析可以去看一下这几篇文章,大概看了一下和2方法中封装的resizeObserver差不多的原理。
https://blog.csdn.net/qq_38377521/article/details/115311799

在这里插入图片描述

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐