beautiful-react-hooks 之 useResizeObserver:声明式监听元素尺寸变化的完整指南
前端开发工具【免费下载链接】beautiful-react-hooks A collection of beautiful and (hopefully) useful React hooks to speed-up your components and hooks development 项目地址https://gitcode.com/gh_mirrors/be/beautiful-react-hooks点击查看免费下载本篇技术指南围绕 beautiful-react-hooks 仓库中的 useResizeObserver 文档 展开系统讲解如何借助浏览器原生 ResizeObserver API 以声明式 Hook 的方式异步监听指定 DOM 元素的尺寸变化并实时获取其 DOMRect 数据。读完本文你将掌握useResizeObserver的完整用法、防抖参数调优方法、源码级生命周期原理与特性支持检测机制并能在自己的 React 组件中直接落地使用。一、为什么需要 useResizeObserver在 Web 开发中容器尺寸变化是一类非常常见但难以优雅处理的需求自适应布局、图表重绘、富文本编辑器高度同步、懒加载占位、Canvas 分辨率适配等场景都需要在元素大小改变时做出响应。传统方案主要依赖window.resize事件但它有两个明显缺陷只能感知窗口尺寸变化无法感知单个元素的尺寸变化例如侧边栏折叠、容器内内容撑开导致的尺寸变化需要手动计算元素尺寸并反复比对代码繁琐且容易产生性能问题。useResizeObserver正是为解决这类问题而生。它封装了浏览器原生 ResizeObserver API正如 useResizeObserver 文档 所述其核心价值在于异步监听在渲染流水线之外异步观察指定 HTML Element 的 DOM Rect 变化不阻塞主线程关键路径自动清理组件卸载时自动销毁观察器disconnect无需开发者手动编写清理逻辑避免内存泄漏。二、安装与引入useResizeObserver是 beautiful-react-hooks 内置 Hook 之一随主包一起安装npm install beautiful-react-hooks从 package.json 的exports字段可以看到该 Hook 支持按需导入的三种模块格式./useResizeObserver: { import: ./dist/esm/useResizeObserver.js, require: ./dist/useResizeObserver.js, types: ./dist/useResizeObserver.d.ts }即既支持 ESM 的import语法也支持 CommonJS 的require语法并附带完整的 TypeScript 类型声明可按如下方式引入import useResizeObserver from beautiful-react-hooks/useResizeObserver;在安装之前请确认项目满足 package.json 中声明的 peerDependencies 版本要求react 18.2.0 20.0.0、react-dom 18.2.0 20.0.0。三、基本用法监听元素尺寸变化useResizeObserver的使用方式非常直观将useRef创建的引用传入 HookHook 会返回当前元素的 DOMRect 数据在首次渲染、尚未触发任何尺寸变化时为undefined。以下示例完整来自 useResizeObserver 文档 的 Basic Usage 章节展示了如何监听一个可缩放的文本域容器并实时展示其尺寸import { useRef } from react; import { Input } from antd; import useResizeObserver from beautiful-react-hooks/useResizeObserver; const ResizeObserverExample () { const ref useRef(); const DOMRect useResizeObserver(ref); return ( DisplayDemo titleuseResizeObserver div ref{ref} Input.TextArea valueResize me / /div {DOMRect ( ul style{{ margin: 20px 0 10px 0, textAlign: left, padding: 0 }} liBox width: {DOMRect.width}/li liBox height: {DOMRect.height}/li liBox left: {DOMRect.left}/li liBox right: {DOMRect.right}/li liBox top: {DOMRect.top}/li liBox bottom: {DOMRect.bottom}/li /ul )} /DisplayDemo ); }; ResizeObserverExample /关键点说明useRef()创建引用并绑定到目标元素ref必须挂载到需要监听的 DOM 节点上示例中是包裹Input.TextArea的divDOMRect初始为undefined因此渲染前需要先做判空DOMRect ...这与源码中useStateDOMRectValues()无初始值的设计一致返回的 DOMRect 包含六个字段width、height、left、right、top、bottom覆盖了元素的几何尺寸与相对位置信息。值得注意的是示例中监听的尺寸来自元素自身contentRect因此当文本域内容变化导致外层div尺寸改变时Hook 会同步触发更新——这正是window.resize事件做不到的。四、返回值详解DOMRectValues 类型从 useResizeObserver 文档 的 Types 章节可以看到Hook 的返回值并非完整的DOMRectReadOnly而是经过精挑细选的六个字段import { type RefObject } from react; export type DOMRectValues PickDOMRectReadOnly, bottom | height | left | right | top | width; /** * Uses the ResizeObserver API to observe changes within the given HTML Element DOM Rect. * param elementRef * param debounceTimeout * returns {undefined} */ declare const useResizeObserver: TElement extends HTMLElement(elementRef: RefObjectTElement, debounceTimeout?: number) DOMRectValues | undefined; export default useResizeObserver;类型签名解析组成含义TElement extends HTMLElement泛型参数限定被监听元素必须是 HTMLElement 及其子类如HTMLDivElement、HTMLTextAreaElementelementRef: RefObjectTElement必传参数指向目标 DOM 元素的 ref 对象debounceTimeout?: number可选参数回调防抖延时毫秒不传则使用默认值返回值DOMRectValues \| undefined六字段几何数据初始渲染时为undefinedDOMRectValues通过 TypeScript 的Pick工具类型从DOMRectReadOnly中挑选字段保证类型安全的同时去掉了x、y、toJSON等不常用成员让返回值更聚焦、更轻量。五、防抖机制与自定义超时时间元素尺寸在拖拽、动画等场景下会高频触发回调若每次变化都触发 React 重渲染会造成不必要的性能开销。因此useResizeObserver内部采用了防抖debounce回调将连续发生的尺寸变化合并为一次最终状态更新。默认超时时间注意文档与源码的差异useResizeObserver 文档 的 Debounce timeout 章节称默认超时为250ms而实际源码 src/useResizeObserver.ts 中参数默认值定义为const useResizeObserver TElement extends HTMLElement (elementRef: RefObjectTElement, debounceTimeout: number 100): DOMRectValues | undefined {以当前仓库源码为准默认防抖时间为 100ms。文档描述与实现存在出入建议在实际项目中显式传入你期望的超时值避免依赖默认行为。自定义超时示例文档展示了通过第二个参数覆盖默认超时的完整用法例如设置为 1000msimport { useRef } from react; import useResizeObserver from beautiful-react-hooks/useResizeObserver; const ResizeObserverExample () { const ref useRef(); const DOMRect useResizeObserver(ref, 1000); return ( DisplayDemo titleuseResizeObserver div ref{ref} Input.TextArea valueResize me / /div {DOMRect ( ul style{{ margin: 20px 0 10px 0, textAlign: left, padding: 0 }} liBox width: {DOMRect.width}/li liBox height: {DOMRect.height}/li liBox left: {DOMRect.left}/li liBox right: {DOMRect.right}/li liBox top: {DOMRect.top}/li liBox bottom: {DOMRect.bottom}/li /ul )} /DisplayDemo ); }; ResizeObserverExample /参数调优建议高频变化 对延迟不敏感如拖拽预览、动画跟随可适当增大超时值如 2501000ms减少重渲染次数需要尽量实时反馈如图表重绘可传 0 或较小值让状态更新更及时测试用例 test/useResizeObserver.spec.js 中即使用了useResizeObserver(refMock, 0)来加速验证流程超时值本质上是lodash.debounce的 wait 参数语义与其他防抖工具一致。六、源码级原理剖析深入 src/useResizeObserver.ts 的实现可以看到 Hook 内部经历了特性检测 → 创建观察器 → 挂载观察目标 → 清理回收四个阶段。1. 特性检测与提前返回const isSupported isApiSupported(ResizeObserver) const observerRef useRefResizeObserver | null(null) const [DOMRect, setDOMRect] useStateDOMRectValues() if (isClient !isSupported) { warnOnce(errorMessage) return undefined }isApiSupported(ResizeObserver)来自 src/shared/isAPISupported.ts其实现为api in window即在客户端环境中检测window.ResizeObserver是否存在若在客户端但 API 不被支持会调用warnOnce输出一次性警告并直接返回undefinedwarnOnce实现在 src/shared/warnOnce.ts使用 Map 缓存已提示过的消息保证同一警告只打印一次避免刷屏警告文案明确指出该错误既可能源于浏览器不支持也可能源于服务端渲染SSR环境下调用。2. 挂载时创建防抖观察器useEffect(() { if (isSupported) { const fn debounce((entries) { const { bottom, height, left, right, top, width } entries[0].contentRect setDOMRect({ bottom, height, left, right, top, width }) }, debounceTimeout) observerRef.current new ResizeObserver(fn) return () { fn.cancel() if (observerRef.current isFunction(observerRef?.current?.disconnect)) { observerRef.current.disconnect() } } } return () {} }, [])这里有几个值得注意的实现细节防抖来自 lodash仓库依赖lodash.debounce^4.0.8见 package.json回调会在连续触发后等待debounceTimeout毫秒才真正执行数据来源是entries[0].contentRectResizeObserver回调会收到ResizeObserverEntry[]数组代码取第一个条目的contentRect即元素内容盒区域解构出六个字段后写入 state清理逻辑成对出现fn.cancel()取消尚未执行的防抖回调disconnect()断开观察器与元素的关联两者共同确保组件卸载后不会再有残留的异步更新空依赖数组[]观察器只在挂载时创建一次整个生命周期复用同一个实例避免反复创建销毁。3. 观察目标元素的绑定useEffect(() { if (isSupported elementRef.current) { if (observerRef.current isFunction(observerRef?.current?.observe)) { observerRef.current.observe(elementRef.current) } } }, [elementRef.current])第二个useEffect依赖elementRef.current当 ref 挂载到真实 DOM 节点后调用ResizeObserver.observe(element)开始观察通过isFunction守卫见 src/shared/isFunction.ts确保observe方法存在再调用增强了跨环境健壮性由于依赖是elementRef.current当 ref 指向的节点切换时观察目标也会自动更新。4. 返回状态最终 Hook 返回DOMRectstate即最新的六字段几何数据供组件渲染使用。整个过程完全声明式开发者只需提供 ref其余创建、观察、防抖、清理均由 Hook 内部完成。七、特性支持与 SSR 注意事项useResizeObserver对运行环境做了严格的前置判断核心依据是 src/shared/isClient.ts 中的isClient常量const isClient !!( typeof window ! undefined window.document window.document.createElement )结合isApiSupported的检查逻辑可以梳理出 Hook 在不同环境下的行为运行环境isClientResizeObserver存在行为现代浏览器客户端true是正常创建观察器并返回 DOMRect 数据旧浏览器客户端true否输出一次性警告返回undefined不抛错SSR / Node 环境false否条件不满足直接返回undefined避免引用window导致崩溃这个设计让 Hook 可以安全地在同构应用中使用服务端渲染时返回undefined不会因访问不存在的window.ResizeObserver而报错客户端水合后则正常开始观察。八、测试用例如何验证行为仓库为useResizeObserver编写了专门的测试文件 test/useResizeObserver.spec.js从两个维度验证了核心行为可作为理解 Hook 语义的补充参考正常路径使用 test/mocks/ResizeObserver.mock.js 模拟原生ResizeObserver通过ResizeObserver.simulateResize()触发尺寸变化回调注入contentRect: { bottom: 10, height: 10, ... }随后验证 Hook 返回值从undefined变为包含几何数据的对象不支持路径删除全局ResizeObserver后调用 Hook验证console.warn被调用且返回值保持undefined。测试中还利用promiseDelay(250)等待防抖回调执行完毕见 test/utils/promiseDelay.js佐证了防抖机制确实生效——尺寸变化并非立即写入 state而是在超时结束后才更新。九、典型应用场景与注意事项综合文档与源码useResizeObserver适合以下场景响应式图表/Canvas 组件容器尺寸变化时重新绘制图形自适应编辑器文本内容撑开容器时同步调整内部布局布局联动侧边栏折叠、面板展开时联动调整其他模块懒加载与占位元素进入可视区或尺寸变化时加载对应资源。使用时的注意事项务必判空后再访问字段首次渲染时返回undefined直接访问DOMRect.width会抛错ref 必须已挂载如果elementRef.current始终为空观察不会生效注意防抖带来的延迟感默认超时较小源码为 100ms如需明显减少重渲染频率可显式调大依赖原生 API需要目标浏览器支持ResizeObserverHook 只负责封装与优雅降级不会引入 polyfill不要手动创建多个观察器Hook 内部已通过observerRef复用单一实例外部无需也无法干预。结语useResizeObserver是 beautiful-react-hooks 中把原生ResizeObserverAPI 转化为声明式 React 能力的典型实现useRef定位元素、防抖合并高频回调、useEffect管理观察器生命周期、特性检测实现 SSR 安全。通过本文的文档 源码 测试三重对照你不仅能熟练使用它也能透彻理解其内部运行机制从而在自己的组件中写出更健壮的响应式逻辑。赞分享前端开发工具【免费下载链接】beautiful-react-hooks A collection of beautiful and (hopefully) useful React hooks to speed-up your components and hooks development 项目地址https://gitcode.com/gh_mirrors/be/beautiful-react-hooks点击查看免费下载相关推荐元素尺寸变化监测beautiful-react-hooks的useResizeObserver hooks完全指南元素尺寸变化监测beautiful react hooks的useResizeObserver hooks完全指南 在现代前端开发中实时监测DOM元素尺寸变前端开发工具beautiful-react-hooks useGlobalEvent为 window 事件监听编写声明式 React Hookbeautiful react hooks useGlobalEvent为 window 事件监听编写声明式 React Hook useGlobalEven前端开发工具react-use 之 useMeasure基于 ResizeObserver 的响应式元素尺寸监听 Hook 实战指南react use 之 useMeasure基于 ResizeObserver 的响应式元素尺寸监听 Hook 实战指南 导读 useMeasure 是 re前端上一篇从Modern.js Builder迁移到Rsbuild的完整指南下一篇Rsbuild 从 0.x 迁移到 1.0 的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考