实现图片懒加载指令v-lazy

1,231 阅读1分钟

懒加载定义

图片懒加载其实就是延迟加载。也就是不用一次性加载所有的图片,等到用户需要某张图片的时候再加载,这样可以避免在同一时间请求大量的数据。也就是当图片滚动到可视区域的时候再去加载图片。

指令

Vue中除了平时常用的v-show、v-bind等指令外,还可以自定义指令。由于自定义指令过于简单,这里只是大致说一下用得到的钩子函数的作用。

  • bind:只调用一次,指令绑定到元素时调用,可以用来初始化。
  • inserted:被绑定的元素插入到父节点时调用。

实现

component文件夹中新建LazyLoad文件夹,在文件夹里新建index.js

代码如下:

const LazyLoad = {
  // install方法
  install(Vue, options) {
    // 代替图片的loading图
    let defaultSrc = options.default;
    Vue.directive('lazy', {
      bind(el, binding) {
        LazyLoad.init(el, binding.value, defaultSrc);
      },
      inserted(el) {
        // 兼容处理
        if ('IntersectionObserver' in window) {
          LazyLoad.observe(el);
        } else {
          LazyLoad.listenerScroll(el);
        }
      },
    })
  },
  // 初始化
  init(el, val, def) {
    // data-src 储存真实src
    el.setAttribute('data-src', val);
    // 设置src为loading图
    el.setAttribute('src', def);
  },
  // 利用IntersectionObserver监听el
  observe(el) {
    let io = new IntersectionObserver(entries => {
      let realSrc = el.dataset.src;
      if (entries[0].isIntersecting) {
        if (realSrc) {
          el.src = realSrc;
          el.removeAttribute('data-src');
        }
      }
    });
    io.observe(el);
  },
  // 监听scroll事件
  listenerScroll(el) {
    let handler = LazyLoad.throttle(LazyLoad.load, 300);
    LazyLoad.load(el);
    window.addEventListener('scroll', () => {
      handler(el);
    });
  },
  // 加载真实图片
  load(el) {
    let windowHeight = document.documentElement.clientHeight
    let elTop = el.getBoundingClientRect().top;
    let elBtm = el.getBoundingClientRect().bottom;
    let 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) {
      let currTime = Date.now();
      let 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;
 

使用

main.js里添加

import LazyLoad from './components/LazyLoad';
Vue.use(LazyLoad, {
  default: 'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7f2421e0269d41299dad3ff4bd0dced9~tplv-k3u1fbpfcp-watermark.image?'
});

在组件中使用

 <img v-lazy="'https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7f2421e0269d41299dad3ff4bd0dced9~tplv-k3u1fbpfcp-watermark.image?'" />

大功告成!以此类推可以自定义很多指令如v-permission等等