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.youkuaiyun.com/qq_38377521/article/details/115311799