在 Vue 中,我们可以利用 IntersectionObserver API 来检测元素是否进入或离开视口,从而实现懒加载、无限滚动、触发动画等效果。下面简单介绍几种常见的应用场景和使用方式:
1. 懒加载图片
借助 IntersectionObserver,我们可以监听图片元素何时进入视口。通常的做法是在组件中设置一个初始的“加载中”图片,当目标图片进入视口时,再将真实的图片地址赋值给 img 标签的 src 属性。例如:
<template>
<img
class="lazy-image"
:src="loadingImage"
:data-src="realSrc"
alt="图片描述"
/>
</template>
<script>
export default {
props: {
realSrc: {
type: String,
required: true
}
},
data() {
return {
loadingImage: '/assets/loading.png'
}
},
mounted() {
const img = this.$el;
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
img.src = img.dataset.src;
observer.unobserve(img);
}
});
}, {
threshold: 0.25 // 当图片有25%进入视口时触发回调
});
observer.observe(img);
}
}
</script>
这样只有当图片进入视口时才会加载真实图片,节省带宽并加快首屏渲染。
2. 无限滚动加载
在列表或内容较多的页面中,可以在底部放置一个“哨兵”元素。当该元素进入视口时,就触发数据加载的逻辑。示例代码如下:
<template>
<div class="list-container">
<div v-for="item in items" :key="item.id" class="list-item">
{{ item.content }}
</div>
<!-- 哨兵元素 -->
<div ref="loadMore" class="load-more">加载中...</div>
</div>
</template>
<script>
export default {
data() {
return {
items: [],
totalCount: 100,
observer: null
}
},
mounted() {
this.loadData(); // 初始加载部分数据
this.observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && this.items.length < this.totalCount) {
this.loadData();
}
});
this.observer.observe(this.$refs.loadMore);
},
beforeDestroy() {
this.observer && this.observer.disconnect();
},
methods: {
loadData() {
// 模拟异步加载数据
setTimeout(() => {
const newItems = Array.from({ length: 10 }, (_, index) => ({
id: this.items.length + index + 1,
content: `第 ${this.items.length + index + 1} 项`
}));
this.items = [...this.items, ...newItems];
}, 500);
}
}
}
</script>
当“加载中…”的哨兵元素进入视口时,就会自动触发 loadData()
方法,达到无限滚动加载的效果。
3. 自定义指令封装
你还可以将 IntersectionObserver 封装为 Vue 自定义指令,使其在多个组件中复用。一个简单的指令示例:
// lazyload.js
export default {
inserted(el, binding) {
const options = {
threshold: 0.1
};
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
el.src = binding.value;
observer.unobserve(el);
}
});
}, options);
observer.observe(el);
}
}
在 main.js 中全局注册后,即可在模板中直接使用:
<template>
<img v-lazyload="'https://example.com/real-image.jpg'" src="/assets/loading.png" alt="图片">
</template>
这种方式封装后可以让代码更加模块化和复用。
总结
IntersectionObserver API 提供了一种高效、异步监听元素可见性的方案,在 Vue 中常用于:
- 图片懒加载(减少首屏加载时间)
- 无限滚动加载(改善用户体验)
- 触发进入视口动画或数据加载
使用时注意在组件销毁前断开观察器,避免内存泄漏。通过合理配置 threshold
和 rootMargin
,你可以灵活控制回调触发的时机,从而达到更佳的性能优化效果。