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];
},
};
关键点解析
-
Polyfill 引入:
import ResizeObserver from "resize-observer-polyfill";使用 polyfill 确保在不支持
ResizeObserver的浏览器中也能正常工作。 -
DOM 节点查找:
const findDOMNode = (instance) => { let node = instance?.$vnode?.elm || instance.$el; if (node && node.tagName) { return node; } return undefined; };这个工具函数用于安全地获取 Vue 组件对应的 DOM 元素。
-
生命周期管理:
mounted和updated钩子中调用registerObserverbeforeDestroy钩子中调用destoryObserver进行清理 确保观察器在组件生命周期内正确初始化和销毁。
-
观察器注册逻辑:
- 检查
disabled属性决定是否禁用观察 - 检查目标元素是否变化,变化时重新创建观察器
- 只在必要时创建新观察器
- 检查
-
事件触发:
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>
性能优化
- 按需观察:通过
disabled属性可以动态启用/禁用观察 - 观察器复用:只在目标元素变化时重新创建观察器
- 及时清理:组件销毁时断开观察器连接,避免内存泄漏
浏览器兼容性
虽然现代浏览器已原生支持 ResizeObserver,但为了兼容旧版浏览器,我们使用了 resize-observer-polyfill。这个 polyfill 实现了类似的功能,确保在所有浏览器中行为一致。
总结
通过封装 ResizeObserver 为 Vue2 组件,我们可以在项目中方便地监听元素尺寸变化,实现更灵活的响应式布局。这种封装方式具有以下优点:
- 组件化设计,使用简单
- 自动管理观察器生命周期
- 提供禁用功能,优化性能
- 兼容多种浏览器环境
这种模式也可以应用于其他 Observer API 的封装,如 IntersectionObserver 等,为 Vue2 项目添加现代化的浏览器能力。