别再只用resize监听元素尺寸变化了,试试ResizeObserver!

前端 潘老师 2天前 11 ℃ (0) 扫码查看

前端开发监听元素尺寸变化是个常见需求,以往,不少开发者会用轮询或者window.resize来实现这个功能。但今天要给大家介绍一个更专业、更高效的原生API——ResizeObserver,要是你还没用过,那可就真的out啦!

一、ResizeObserver解决了什么难题?

ResizeObserver出现之前,监听元素尺寸变化的方法有不少,像window.addEventListener('resize') 、用定时器轮询DOM尺寸、借助MutationObserver搭配逻辑判断,还有使用第三方库等。不过,这些方法都存在一个“硬伤”:它们监听的其实是“窗口变化”或者“DOM结构变化”,没办法精准地监听元素自身尺寸的变化。就拿window.addEventListener('resize')来说吧,看下面这段代码:

// 👎 无法监听特定元素尺寸变化
window.addEventListener('resize', () => {
  console.log('窗口变化了,但某个元素变没变不知道');
});

从这段代码可以看出,窗口变化时,我们根本不知道页面里某个特定元素的尺寸有没有变化。

ResizeObserver的出现,就是专门来解决这个问题的。看下面的代码:

const ro = new ResizeObserver(entries => {
  for (let entry of entries) {
    console.log('元素尺寸变化:', entry.contentRect);
  }
});

ro.observe(document.querySelector('#target'));

ResizeObserver基于浏览器底层布局机制,能异步执行操作,对性能十分友好,它专注于监听DOM元素尺寸本身的变化。只要被监听的元素尺寸有变动,就能及时捕捉到,并且获取到变化后的尺寸信息。

二、ResizeObserver的应用场景

(一)图表重绘

在使用像ECharts、Highcharts这些图表库时,当图表容器的尺寸发生变化,比如缩放、切换布局或者隐藏后再显示,如果不及时处理,图表可能会被挤压变形,影响展示效果。这时候ResizeObserver就派上用场了。看下面的代码:

const chart = echarts.init(document.getElementById('chart'));

const ro = new ResizeObserver(() => {
  chart.resize();
});
ro.observe(document.getElementById('chart'));

这段代码中,通过ResizeObserver监听图表容器的尺寸变化,一旦容器尺寸改变,就会自动调用chart.resize()方法重新绘制图表,让图表始终保持良好的展示效果,再也不用依赖window.resize啦!

(二)响应式布局组件

对于按钮组、菜单、卡片布局这类组件,在容器宽度发生变化时,可能需要切换布局模式。用ResizeObserver来实现,逻辑会更加清晰。比如下面这段代码:

ro.observe(container);
ro.callback = entries => {
  const width = entries[0].contentRect.width;
  toggleLayout(width > 600 ? 'row' : 'column');
};

在这段代码里,通过监听容器元素的尺寸变化,根据宽度来决定是采用row还是column布局模式,比起传统的resize + getBoundingClientRect()方式,代码简洁明了不少。

(三)复杂布局场景

在嵌套iframe、自适应容器等复杂布局场景中,ResizeObserver的优势就更加明显了。像iframe中的元素尺寸变化,用window是捕捉不到的,但ResizeObserver却可以轻松实现。而且,它还支持组件嵌套、shadow DOM,在组合微前端、构建复杂布局系统时都能发挥重要作用。

三、ResizeObserver与其他监听方式的对比

为了让大家更直观地了解ResizeObserver的优势,下面通过表格对比一下它和其他监听方式:

功能需求 ResizeObserver window.resize MutationObserver setInterval
元素尺寸变化监听 ✅ 支持 ❌ 不支持 ⚠️ 需判断尺寸 ⚠️ 不推荐
性能表现 ⭐⭐⭐⭐ ⭐⭐ ⭐⭐
精确度
实时性
编码复杂度

从表格中可以看出,在监听元素尺寸变化方面,ResizeObserver无论是性能、精确度还是实时性,都表现得相当出色,编码复杂度也较低。

四、ResizeObserver高阶使用技巧

(一)防抖处理:控制触发频率

在一些高频率布局变化的场景,比如拖动元素或者动态内容渲染时,频繁触发ResizeObserver的回调函数可能会导致性能问题。这时候可以通过防抖处理来控制触发频率。看下面的代码:

const debounce = (fn, delay = 100) => {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
};
const ro = new ResizeObserver(debounce(entries => {
  // 执行逻辑
}));

在这段代码里,debounce函数实现了防抖功能,当ResizeObserver监听到尺寸变化时,并不会立即执行回调函数,而是等待delay时间(这里设置为100毫秒),如果在这段时间内又有新的变化,就会取消之前的定时器重新计时,只有当超过delay时间没有新变化时,才会执行回调函数,这样就能有效控制触发频率了。

(二)多元素监听 + 状态管理

在处理组件列表、自定义容器池、栅格系统这类场景时,可能需要同时监听多个元素的尺寸变化,并管理它们的状态。下面这段代码展示了如何实现:

const sizeMap = new Map();
const ro = new ResizeObserver(entries => {
  for (const entry of entries) {
    const old = sizeMap.get(entry.target);
    const now = entry.contentRect;
    if (!old || old.width !== now.width) {
      sizeMap.set(entry.target, now);
      handleResize(entry.target);
    }
  }
});

这里使用Map来存储每个元素的尺寸状态,当ResizeObserver监听到元素尺寸变化时,会对比当前尺寸和之前存储的尺寸,如果有变化,就更新状态并执行相应的处理函数handleResize

(三)结合生命周期动态绑定(Vue、React)

在Vue、React或者Web Component开发中,为了避免内存泄漏,需要在组件的生命周期中正确地绑定和解绑ResizeObserver 。看下面的代码:

onMounted(() => {
  ro.observe(elRef.value);
});
onBeforeUnmount(() => {
  ro.unobserve(elRef.value);
});

在组件挂载时,通过onMounted钩子函数监听元素尺寸变化;在组件卸载前,利用onBeforeUnmount钩子函数取消监听,这样就能确保内存的合理使用,避免出现内存泄漏问题。

五、使用ResizeObserver的注意事项

虽然ResizeObserver功能强大,但在使用过程中也有一些需要注意的地方:

  • 避免同步读取布局:尽量不要在回调函数中同步读取布局相关属性,比如offsetHeight 。因为这样可能会引发强制回流,导致性能抖动,影响页面的流畅性。
  • 控制多元素监听频率:当监听多个元素尺寸变化时,最好结合防抖或者节流技术,控制回调函数的触发频率,防止因为频繁回调而造成页面卡顿。
  • 正确绑定和解绑:在组件开发中,一定要在组件的生命周期里正确地绑定和解绑ResizeObserver ,防止出现内存泄漏,保证程序的稳定性。
  • 兼容性问题:IE浏览器不支持ResizeObserver ,如果项目需要兼容IE,可以使用Polyfill进行兼容处理,比如resize-observer-polyfill

总的来说,ResizeObserver是一个轻量、精准且高效的尺寸变化监听方案,希望大家都能试试这个强大的API!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/front/17914.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】