Vue3 封装 IntersectionObserver
IntersectionObserver 是浏览器原生交叉观察 API异步监测目标元素和容器视口是否相交。用来替代 scroll 事件 getBoundingClientRect 方案避免高频同步重排性能更好。 典型用途图片懒加载、曝光埋点、无限滚动、虚拟列表可视判断开发注意组件卸载销毁实例避免内存泄漏DOM 复用时要处理监听错乱问题。问虚拟列表里面结合图片懒加载使用 IntersectionObserver 有什么坑 答虚拟列表 DOM 是复用的同一个 DOM 先后渲染不同数据。元素离开视口再次复用时要区分当前绑定的数据防止旧图片请求完成覆盖新内容同时做好实例销毁防止内存泄露。template div classscroll-container refscrollBox div v-foritem in list :keyitem.id refitemRefs {{ item.name }} /div /div /template script setup import { ref, onMounted, onUnmounted, watch } from vue /** * param callback 交叉回调 * param options observer配置 {root, rootMargin, threshold} */ function useIntersection(callback, options {}) { const observer ref(null) // 收集被观察的DOM const observeList new Set() // 创建观察者 function createObserver() { observer.value new IntersectionObserver((entries) { entries.forEach(entry { callback(entry) }) }, options) } // 监听DOM function observe(el) { if (!observer.value) createObserver() if (!observeList.has(el)) { observeList.add(el) observer.value.observe(el) } } // 取消监听单个元素 function unobserve(el) { if (observer.value observeList.has(el)) { observer.value.unobserve(el) observeList.delete(el) } } // 全部销毁 function destroy() { if (observer.value) { observer.value.disconnect() observer.value null } observeList.clear() } onUnmounted(() destroy()) return { observe, unobserve, destroy } } // 使用示例 const scrollBox ref(null) const itemRefs ref([]) const { observe } useIntersection((entry) { if (entry.isIntersecting) { console.log(元素进入视口, entry.target) } }, { root: null, // 默认视口如果是容器滚动root: scrollBox.value rootMargin: 100px, // 提前100px触发懒加载预加载 threshold: 0 }) // 等DOM渲染完成批量监听 onMounted(() { itemRefs.value.forEach(el observe(el)) }) /scriptref 数组获取时机问题v-for绑定 ref 数组需要在nextTick内执行监听保证 DOM 已经挂载。虚拟列表 DOM 复用致命问题同一个 DOM 节点不断复用渲染不同数据不要只依靠进入视口逻辑元素复用时需要清除上一次图片请求 / 状态回调中区分当前 DOM 绑定的数据避免旧图片异步加载覆盖新内容。自定义滚动容器 root 设置虚拟列表滚动不是 window必须传入滚动容器 DOM 作为root否则观察永远不会触发options: { root: scrollWrapDom, // 列表外层滚动容器 rootMargin: 50px }内存泄漏头号元凶组件销毁不调用disconnect()观察者持续持有 DOM 引用页面内存持续上涨。v-if 元素元素被 v-if 销毁后一定要执行unobserve不然观察器监听不存在节点