图片懒加载

120 阅读2分钟

方式1:监听元素scroll

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }

    img {
      display: block;
      height: 50vh;
      width: 50vw;
      border: 1px solid #000;
    }

    #app {
      height: 100vh;
      overflow: auto
    }

  </style>
</head>
<script src="https://unpkg.com/vue@3.0.5/dist/vue.global.js"></script>

<body>
  <div id="app">
    <button @click="arrChange">去掉</button>
    <img v-for="i in [1,2,3,4,5,6]" :key="i" :alt="i" v-lazy="imgUrl" alt="">
  </div>
  <script>
    var vue3Composition = {
      setup () {
        var imgUrl = 'https://sngedu-punch-1251502357.cos.ap-guangzhou.myqcloud.com/agency_picurl/QFzQYCgCrxnEM2K8pFvSckZV2zcSPicxDYYIfgqvqd3IOlbBggQialjAV0mcfXYVeo'
        return {
          imgUrl
        }
      },
    }
    let dom = document.getElementById('app')
    var ObserverList = []
    var vm = Vue.createApp(vue3Composition)

    var scrollFun = (() => {
      // 初始化已经显示图片
      var initUrl = num => {
          ObserverList.forEach(({el, value}, index) => {
            if (el.offsetTop - 150 < num) {
              // 元素高度小于 num 加载
              el.src = value
              ObserverList.splice(index, 1)
            }
          })
      }
      return () => {
        initUrl(dom.scrollTop + document.body.clientHeight)
        dom.addEventListener('scroll', () => {
          initUrl(dom.scrollTop + document.body.clientHeight)
        })
      }
    })()

    vm.directive('lazy', {
      mounted(el, {value}) {
        // console.log(el.offsetTop)
        ObserverList.push({el ,value})
        scrollFun()
      }
    })

    vm.mount('#app')

  </script>
</body>

</html>

方式2:利用IntersectionObserver监听元素出现在视口中

// 图片懒加载自定义指令 | vue3版本
import baseImg from '@/assets/logo.png'

let timer = null
// 创建监听器
let observer = new IntersectionObserver((entries) => {
  // entries是所有被监听对象的集合
  entries.forEach((entry) => {
    if (entry.isIntersecting || entry.intersectionRatio > 0) {
      // 当被监听元素到临界值且未加载图片时触发。
      !entry.target.isLoaded && showImage(entry.target, entry.target.data_src)
    }
  })
})
function showImage(el, imgSrc) {
  // setTimeout查看效果
  // setTimeout(() => {
  console.log('1')
  const img = new Image()
  img.src = imgSrc
  img.onload = () => {
    el.src = imgSrc
    el.isLoaded = true
  }
  // }, 1000)
}
export default {
  mounted (el, binding, vnode) {
    clearTimeout(timer)
    // 初始化时展示默认图片
    el.src = baseImg
    // 将需要加载的图片地址绑定在dom上
    el.data_src = binding.value
    observer.observe(el)

    // 防抖,这里在组件卸载的时候停止监听
    const vm = vnode.context
    // console.log(vm)
    timer = setTimeout(() => {
      vm.$on('hook:beforeDestroy', () => {
        observer.disconnect()
      })
    }, 20)
  }
}

方式3 插件vueuse

url:vueuse.org/core/useInt…

效果:图片进入到视口才加载(不理想)

参考代码(vue3)

// main.js
import directives from './directives';

app.use(directives)

app.mount('#app')

// directives.js
import { useIntersectionObserver } from '@vueuse/core'
export default {
  install (app) {
    app.directive('img-lazy', {
      mounted(el , binding){
        //el指令绑定到的元素。这可以用于直接操作 DOM
        //binding: binding.value 指令等于后面绑定表达式的值 图片url
        const { stop } = useIntersectionObserver(el, ([{ isIntersecting }]) => {
          if (isIntersecting) {
            // console.log(true);
            el.src = binding.value;
            // 停止监听
            stop();
          }
        });
      }
    })
  }
}

方式4 插件 vue-lazyload

url:github.com/hilongjw/vu…

效果:图片进入到视口前加载,效果较好,适合滑动加载,

缺点:会造成1张图片2次加载,暂时没有解决办法

参考代码(vue3)

// main.js
import VueLazyload from 'vue-lazyload'
import loadImage from '@/assets/images/temp.png'

app.use(VueLazyload, {
  preLoad: 1.3, //预加载的宽高比
  loading: loadImage, //图片加载状态下显示的图片
  // error: errorimage, //图片加载失败时显示的图片
  attempt: 1, // 加载错误后最大尝试次数
})