Vue 3 和 IntersectionObserver API 的结合可以实现高效地观察 DOM 元素是否进入视口,从而优化性能和用户体验。本文将深入解析如何在 Vue 3 中使用 IntersectionObserver API,并通过实际代码示例展示其用法。
IntersectionObserver 是一个现代浏览器 API,用于高效地检测目标元素是否与视口或其他祖先元素发生交集(即进入或离开视口)。相比传统的 scroll
或 resize
事件监听器,IntersectionObserver 提供了更高效的解决方案,因为它不需要频繁触发回调函数。
[0, 1]
,表示从完全不相交到完全相交的状态。在 Vue 3 中,我们可以利用 Composition API 或 Options API 来集成 IntersectionObserver。以下分别介绍两种方式的实现。
Composition API 提供了更灵活的方式管理状态和逻辑,适合复杂场景。
<template>
<div>
<img v-for="(src, index) in images" :key="index" :src="getSrc(src)" @load="onLoad(index)" />
</div>
</template>
<script>
import { ref, onMounted, onUnmounted } from 'vue';
export default {
setup() {
const images = ref([
'https://example.com/image1.jpg',
'https://example.com/image2.jpg',
// 更多图片...
]);
const observer = ref(null);
const loadedImages = ref([]);
const getSrc = (src) => {
return loadedImages.value.includes(src) ? src : '';
};
const onLoad = (index) => {
loadedImages.value.push(images.value[index]);
};
const observeImage = (el) => {
if (observer.value) {
observer.value.observe(el);
}
};
onMounted(() => {
observer.value = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.value.unobserve(img); // 停止观察
}
});
}, { threshold: 0.1 });
document.querySelectorAll('img').forEach(observeImage);
});
onUnmounted(() => {
if (observer.value) {
observer.value.disconnect();
}
});
return { images, getSrc };
},
};
</script>
IntersectionObserver
监听每个图片元素。data-src
属性赋值给 src
,从而实现懒加载。disconnect()
方法清理观察器。Options API 更加直观,适合简单场景。
<template>
<div>
<ul>
<li v-for="(item, index) in items" :key="index">{{ item }}</li>
<div ref="observerTarget">加载更多...</div>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
items: Array.from({ length: 20 }, (_, i) => `Item ${i + 1}`),
observer: null,
};
},
mounted() {
this.observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.loadMoreItems();
}
});
}, { threshold: 0.5 });
this.observer.observe(this.$refs.observerTarget);
},
beforeDestroy() {
if (this.obswer) {
this.observer.disconnect();
}
},
methods: {
loadMoreItems() {
const newItemCount = 10;
const start = this.items.length + 1;
const newItems = Array.from({ length: newItemCount }, (_, i) => `Item ${start + i}`);
this.items = [...this.items, ...newItems];
},
},
};
</script>
ref
获取需要观察的目标元素。loadMoreItems
方法加载更多数据。IntersectionObserver 不仅可以观察视口中的元素,还可以观察某个容器内的子元素。
const options = {
root: document.querySelector('.custom-container'), // 指定根元素
rootMargin: '0px', // 边距调整
threshold: 0.5, // 阈值
};
const observer = new IntersectionObserver(callback, options);
可以通过 unobserve
和重新 observe
来动态调整观察选项。
observer.unobserve(targetElement);
observer.observe(targetElement, updatedOptions);
disconnect()
方法释放资源。