自定义指令懒加载

v-LazyLoad


背景:在类电商类项目,往往存在大量的图片,如 banner 广告图,菜单导航图,美团等商家列表头图等。图片众多以及图片体积过大往往会影响页面加载速度,造成不良的用户体验,所以进行图片懒加载优化势在必行。

需求:实现一个图片懒加载指令,只加载浏览器可见区域的图片。

思路:

图片懒加载的原理主要是判断当前图片是否到了可视区域这一核心逻辑实现的

拿到所有的图片 Dom ,遍历每个图片判断当前图片是否到了可视区范围内

如果到了就设置图片的 src 属性,否则显示默认图片

图片懒加载有两种方式可以实现,一是绑定 srcoll 事件进行监听,二是使用 IntersectionObserver 判断图片是否到了可视区域,但是有浏览器兼容性问题。

下面封装一个懒加载指令兼容两种方法,判断浏览器是否支持 IntersectionObserver API,如果支持就使用 IntersectionObserver 实现懒加载,否则则使用 srcoll 事件监听 + 节流的方法实现。

const LazyLoad = {

  // install方法

  install(Vue, options) {

    const defaultSrc = options.default

    Vue.directive('lazy', {

      bind(el, binding) {

        LazyLoad.init(el, binding.value, defaultSrc)

      },

      inserted(el) {

        if (IntersectionObserver) {

          LazyLoad.observe(el)

        } else {

          LazyLoad.listenerScroll(el)

        }

      },

    })

  },

  // 初始化

  init(el, val, def) {

    el.setAttribute('data-src', val)

    el.setAttribute('src', def)

  },

  // 利用IntersectionObserver监听el

  observe(el) {

    var io = new IntersectionObserver((entries) => {

      const realSrc = el.dataset.src

      if (entries[0].isIntersecting) {

        if (realSrc) {

          el.src = realSrc

          el.removeAttribute('data-src')

        }

      }

    })

    io.observe(el)

  },

  // 监听scroll事件

  listenerScroll(el) {

    const handler = LazyLoad.throttle(LazyLoad.load, 300)

    LazyLoad.load(el)

    window.addEventListener('scroll', () => {

      handler(el)

    })

  },

  // 加载真实图片

  load(el) {

    const windowHeight = document.documentElement.clientHeight

    const elTop = el.getBoundingClientRect().top

    const elBtm = el.getBoundingClientRect().bottom

    const realSrc = el.dataset.src

    if (elTop - windowHeight < 0 && elBtm > 0) {

      if (realSrc) {

        el.src = realSrc

        el.removeAttribute('data-src')

      }

    }

  },

  // 节流

  throttle(fn, delay) {

    let timer

    let prevTime

    return function (...args) {

      const currTime = Date.now()

      const context = this

      if (!prevTime) prevTime = currTime

      clearTimeout(timer)

      if (currTime - prevTime > delay) {

        prevTime = currTime

        fn.apply(context, args)

        clearTimeout(timer)

        return

      }

      timer = setTimeout(function () {

        prevTime = Date.now()

        timer = null

        fn.apply(context, args)

      }, delay)

    }

  },

}

export default LazyLoad

使用,将组件内  标签的 src 换成 v-LazyLoad

你可能感兴趣的:(自定义指令懒加载)