Vue3中实现lazy-img,懒加载图片指令

1. 安装依赖

npm install @vueuse/core

2. 创建懒加载指令

创建 src/directives/imgLazy.js 文件:

import { useIntersectionObserver } from '@vueuse/core'

export const imgLazyDirective = {
  mounted(el, binding) {
    // 设置默认占位图
    el.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIwMCIgaGVpZ2h0PSI1MDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3QgZmlsbD0iI2Y1ZjVmNSIgd2lkdGg9IjEyMDAiIGhlaWdodD0iNTAwIi8+PHRleHQgZmlsbD0iIzc1NzU3NSIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjQwIiBkeT0iMTMuNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iNjAwIiB5PSIyNTAiPkxvYWRpbmcuLi48L3RleHQ+PC9zdmc+' // 占位图
    
    const { stop } = useIntersectionObserver(
      el,
      ([{ isIntersecting }]) => {
        if (isIntersecting) {
          // 元素进入视口,加载真实图片
          el.src = binding.value
          stop() // 停止观察
        }
      }
    )
  }
}

3. 注册全局指令

在 src/main.js 中注册:

import { createApp } from 'vue'
import { imgLazyDirective } from './directives/imgLazy'

const app = createApp(App)
app.directive('img-lazy', imgLazyDirective)

4. 在组件中使用

<script setup>
import { getBannerAPI } from '@/apis/home'
import { onMounted, ref } from 'vue'

const bannerList = ref([])
const getBanner = async () => { 
  const res = await getBannerAPI()
  bannerList.value = res.result
}

onMounted(() => { 
  getBanner()
})
</script>

<template>
  <div class="home-banner">
    <el-carousel height="500px">
      <el-carousel-item v-for="item in bannerList" :key="item.id">
        <img v-img-lazy="item.imgUrl" :alt="item.alt || 'banner image'">
      </el-carousel-item>
    </el-carousel>
  </div>
</template>

<style scoped lang='scss'>
.home-banner {
  width: 1240px;
  height: 500px;
  position: absolute;
  left: 0;
  top: 0;
  z-index: 98;

  img {
    width: 100%;
    height: 500px;
  }
}
</style>

5.关键优势

性能优化:只有当图片即将进入视口时才加载
用户体验:减少初始加载时间
资源节省:避免不必要的网络请求
简单易用:通过指令方式轻松集成

6.使用vue3插件进行优化

main.js直接use
image

// 定义懒加载插件
import { useIntersectionObserver } from '@vueuse/core'

export const lazyPlugin = {
  install (app) {
    // 懒加载指令逻辑
    app.directive('img-lazy', {
      mounted (el, binding) {
        // el: 指令绑定的那个元素 img
        // binding: binding.value  指令等于号后面绑定的表达式的值  图片url
        // console.log(el, binding.value)
        el.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIwMCIgaGVpZ2h0PSI1MDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHJlY3QgZmlsbD0iI2Y1ZjVmNSIgd2lkdGg9IjEyMDAiIGhlaWdodD0iNTAwIi8+PHRleHQgZmlsbD0iIzc1NzU3NSIgZm9udC1mYW1pbHk9IkFyaWFsIiBmb250LXNpemU9IjQwIiBkeT0iMTMuNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgeD0iNjAwIiB5PSIyNTAiPkxvYWRpbmcuLi48L3RleHQ+PC9zdmc+' // 占位图
        const { stop } = useIntersectionObserver( // 检查元素是否进入视口区域
          el,
          ([{ isIntersecting }]) => {
            // console.log(isIntersecting)
            if (isIntersecting) {
              // 进入视口区域
              el.src = binding.value
              stop()
            }
          },
        )
      }
    })
  }
}

posted @ 2025-11-17 15:42  一个小笨蛋  阅读(15)  评论(0)    收藏  举报