在 Vue3 开发中,图片懒加载是优化页面性能、提升用户体验的核心技术之一。通过延迟加载非可视区域的图片,可以显著减少初始加载时间,节省带宽资源。本文ZHANID工具网将详细解析四种主流实现方案,涵盖原生 API、第三方库及自定义指令等多种技术选型。
一、原生 Intersection Observer API 方案
1. 基础实现原理
Intersection Observer API 是浏览器原生提供的交叉观察器,通过监听元素与视口的交叉状态,实现精准的懒加载控制。其核心优势在于:
异步非阻塞:基于事件循环机制,不阻塞主线程
性能优化:相比传统滚动事件监听,CPU 占用降低 80% 以上
精准控制:支持设置根元素、边距阈值等高级参数

2. 代码实现步骤
<template>
<img
v-for="(img, idx) in imageList"
:key="idx"
:data-src="img.url"
ref="lazyImgs"
class="lazy-load"
>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
const imageList = ref([
{ url: 'https://example.com/1.jpg' },
{ url: 'https://example.com/2.jpg' },
// ...更多图片
])
const lazyImgs = ref([])
let observer = null
onMounted(() => {
const callback = (entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
observer.unobserve(img)
}
})
}
const options = {
root: null, // 视口为根元素
rootMargin: '0px', // 边距阈值
threshold: 0.1 // 可见比例达到 10% 触发
}
observer = new IntersectionObserver(callback, options)
lazyImgs.value.forEach(img => observer.observe(img))
})
onBeforeUnmount(() => {
if (observer) observer.disconnect()
})
</script>3. 高级配置技巧
阈值优化:设置
threshold: [0, 0.5, 1]实现多阶段加载根元素指定:在移动端可设置
root: document.querySelector('.scroll-container')预加载控制:通过
rootMargin: '200px'提前 200px 加载
二、VueUse 组合式函数方案
1. 技术优势解析
VueUse 是 Vue 官方推荐的组合式函数库,其 useIntersectionObserver 方法具有以下特点:
响应式集成:完美兼容 Vue3 的 reactivity 系统
类型安全:提供完整的 TypeScript 类型支持
简洁 API:相比原生 API 代码量减少 50%
2. 实现代码示例
npm install @vueuse/core
<template>
<img
v-for="(img, idx) in imageList"
:key="idx"
:data-src="img.url"
v-if="isVisible[idx]"
:src="img.url"
class="lazy-load"
>
</template>
<script setup>
import { ref } from 'vue'
import { useIntersectionObserver } from '@vueuse/core'
const imageList = ref([/* 图片数据 */])
const isVisible = ref(Array(imageList.value.length).fill(false))
imageList.value.forEach((_, idx) => {
const target = ref(null)
const { stop } = useIntersectionObserver(
target,
([{ isIntersecting }]) => {
if (isIntersecting) {
isVisible.value[idx] = true
stop()
}
},
{ threshold: 0.1 }
)
})
</script>3. 性能对比数据
| 方案 | 代码量 | 加载速度 | 内存占用 |
|---|---|---|---|
| 原生 API | 100% | ★★★★☆ | ★★★☆☆ |
| VueUse 组合式函数 | 50% | ★★★★★ | ★★★★☆ |
三、vue3-lazyload 插件方案
1. 插件特性矩阵
| 特性 | 支持情况 |
|---|---|
| 响应式图片 | ✔️ |
| 加载中占位符 | ✔️ |
| 错误处理 | ✔️ |
| 滚动容器指定 | ✔️ |
| SSR 支持 | ✔️ |
2. 集成实施步骤
npm install vue3-lazyload
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import VueLazyload from 'vue3-lazyload'
const app = createApp(App)
app.use(VueLazyload, {
preLoad: 1.3,
error: '/error.png',
loading: '/loading.gif',
attempt: 2,
scrollContainer: '.scroll-wrapper'
})
app.mount('#app')<template> <img v-lazy="imageUrl" alt="懒加载图片"> </template> <script setup> const imageUrl = 'https://example.com/image.jpg' </script>
3. 高级配置示例
app.use(VueLazyload, {
filter: {
webp(listener, options) {
if (options.supportWebp) return listener.src + '?format=webp'
}
},
adapter: {
loaded({ el }) {
el.classList.add('loaded')
},
error({ el }) {
el.style.backgroundColor = '#f00'
}
}
})四、自定义指令方案
1. 指令生命周期
| 生命周期钩子 | 触发时机 |
|---|---|
| created | 指令绑定到元素前 |
| beforeMount | 元素插入父节点前 |
| mounted | 元素插入父节点后 |
| updated | 组件更新后 |
| beforeUnmount | 元素卸载前 |
| unmounted | 元素卸载后 |
2. 指令实现代码
// directives/lazyLoad.js
export default {
mounted(el, binding) {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
const img = new Image()
img.src = binding.value
img.onload = () => {
el.src = binding.value
el.classList.add('fade-in')
}
observer.unobserve(el)
}
}, { threshold: 0.1 })
observer.observe(el)
el._observer = observer
},
unmounted(el) {
if (el._observer) {
el._observer.disconnect()
}
}
}3. 全局注册与使用
// main.js
import lazyLoad from './directives/lazyLoad'
const app = createApp(App)
app.directive('lazy', lazyLoad)<template> <img v-lazy="'https://example.com/image.jpg'" alt="自定义指令加载"> </template>
五、方案选型建议
| 场景 | 推荐方案 | 关键考量因素 |
|---|---|---|
| 简单页面快速实现 | vue3-lazyload 插件 | 开发效率、维护成本 |
| 需要深度定制加载逻辑 | 原生 API 或自定义指令 | 灵活性、代码控制度 |
| 复杂交互项目 | VueUse 组合式函数 | 响应式集成、类型安全 |
| 需支持特殊容器滚动 | 自定义指令 + 滚动容器配置 | 容器边界控制、性能优化 |
六、性能优化实践
图片预加载:设置
preLoad: 1.5提前加载后续图片占位符优化:使用 SVG 占位符替代纯色背景,提升用户体验
缓存策略:配合 Service Worker 实现图片缓存
响应式处理:通过
srcset属性实现多尺寸适配错误重试:设置
attempt: 3增加加载容错
七、常见问题解决方案
1. 图片闪烁问题
原因:占位图与实际图片尺寸不一致
解决方案:
.lazy-load {
min-width: 100px;
min-height: 100px;
background: #f0f0f0 url('/placeholder.svg') no-repeat center;
background-size: 30%;
}2. 移动端滚动卡顿
原因:频繁触发回调导致性能问题
解决方案:
const observer = new IntersectionObserver(entries => {
requestAnimationFrame(() => {
entries.forEach(entry => {
// 加载逻辑
})
})
}, options)3. 动态内容加载
场景:无限滚动列表
解决方案:
watch(() => props.imageList, (newVal) => {
nextTick(() => {
newVal.forEach((img, idx) => {
if (!elRefs.value[idx]._observer) {
observer.observe(elRefs.value[idx])
}
})
})
}, { immediate: true })八、总结
通过本文的系统解析,开发者可以根据具体场景选择最合适的图片懒加载方案。在实际项目中,建议优先采用 VueUse 组合式函数方案,在需要深度定制或特殊容器支持时,可结合原生 API 或自定义指令实现。未来随着浏览器技术的演进,图片懒加载将与更多 Web 性能指标深度集成,形成更智能的资源加载体系。
本文由@战地网 原创发布。
该文章观点仅代表作者本人,不代表本站立场。本站不承担相关法律责任。
如若转载,请注明出处:https://www.zhanid.com/biancheng/4398.html




















