Vue2 中使用 ResizeObserver 实现元素尺寸变化监听

346 阅读3分钟

Vue2 中使用 ResizeObserver 实现元素尺寸变化监听

概述

在现代前端开发中,响应式设计变得越来越重要。我们需要知道元素尺寸何时发生变化,以便做出相应的布局调整。ResizeObserver API 正是为此而生,它允许我们监听元素尺寸的变化。本文将介绍如何在 Vue2 项目中实现一个 ResizeObserver 组件。

ResizeObserver 简介

ResizeObserver 是一个现代的浏览器 API,它可以高效地监听元素内容矩形(content rectangle)的变化。与传统的 window.resize 事件或通过 requestAnimationFrame 轮询检测尺寸变化相比,ResizeObserver 提供了更好的性能和更精确的控制。

Vue2 实现方案

组件代码解析

我们实现了一个 Vue2 组件来封装 ResizeObserver 的功能:

import ResizeObserver from "resize-observer-polyfill";

const findDOMNode = (instance) => {
  let node = instance?.$vnode?.elm || instance.$el;
  if (node && node.tagName) {
    return node;
  }
  return undefined;
};

export default {
  name: "ResizeObserver",
  props: {
    disabled: {
      type: Boolean,
      required: false,
      default: false,
    },
  },
  mounted() {
    this.registerObserver();
  },
  updated() {
    this.registerObserver();
  },
  beforeDestory() {
    this.destoryObserver();
  },
  methods: {
    destoryObserver() {
      if (this.resizeObserver) {
        this.resizeObserver.disconnect();
        this.resizeObserver = null;
      }
    },
    registerObserver() {
      if (this.disabled) {
        this.destoryObserver();
        return;
      }

      const element = findDOMNode(this);
      const isChanged = element != this.element;

      if (isChanged) {
        this.element = element;
        this.destoryObserver();
      }

      if (element && !this.resizeObserver) {
        this.resizeObserver = new ResizeObserver(this.handleResize);
        this.resizeObserver.observe(element);
      }
    },
    handleResize(entries) {
      const entry = entries[0];
      this.$emit("resize", entry);
    },
  },
  render() {
    return this.$slots.default?.[0];
  },
};

关键点解析

  1. Polyfill 引入

    import ResizeObserver from "resize-observer-polyfill";
    

    使用 polyfill 确保在不支持 ResizeObserver 的浏览器中也能正常工作。

  2. DOM 节点查找

    const findDOMNode = (instance) => {
      let node = instance?.$vnode?.elm || instance.$el;
      if (node && node.tagName) {
        return node;
      }
      return undefined;
    };
    

    这个工具函数用于安全地获取 Vue 组件对应的 DOM 元素。

  3. 生命周期管理

    • mountedupdated 钩子中调用 registerObserver
    • beforeDestroy 钩子中调用 destoryObserver 进行清理 确保观察器在组件生命周期内正确初始化和销毁。
  4. 观察器注册逻辑

    • 检查 disabled 属性决定是否禁用观察
    • 检查目标元素是否变化,变化时重新创建观察器
    • 只在必要时创建新观察器
  5. 事件触发

    handleResize(entries) {
      const entry = entries[0];
      this.$emit("resize", entry);
    }
    

    当元素尺寸变化时,通过 Vue 的自定义事件机制通知父组件。

使用示例

在父组件中使用这个 ResizeObserver 组件:

<template>
  <ResizeObserver @resize="handleResize">
    <div>可调整大小的内容</div>
  </ResizeObserver>
</template>

<script>
import ResizeObserver from './ResizeObserver';

export default {
  components: { ResizeObserver },
  methods: {
    handleResize(entry) {
      console.log('元素尺寸变化:', entry.contentRect);
      // 根据新尺寸调整布局
    }
  }
}
</script>

性能优化

  1. 按需观察:通过 disabled 属性可以动态启用/禁用观察
  2. 观察器复用:只在目标元素变化时重新创建观察器
  3. 及时清理:组件销毁时断开观察器连接,避免内存泄漏

浏览器兼容性

虽然现代浏览器已原生支持 ResizeObserver,但为了兼容旧版浏览器,我们使用了 resize-observer-polyfill。这个 polyfill 实现了类似的功能,确保在所有浏览器中行为一致。

总结

通过封装 ResizeObserver 为 Vue2 组件,我们可以在项目中方便地监听元素尺寸变化,实现更灵活的响应式布局。这种封装方式具有以下优点:

  1. 组件化设计,使用简单
  2. 自动管理观察器生命周期
  3. 提供禁用功能,优化性能
  4. 兼容多种浏览器环境

这种模式也可以应用于其他 Observer API 的封装,如 IntersectionObserver 等,为 Vue2 项目添加现代化的浏览器能力。