乐趣区

手拉手带你实现一个图片懒加载指令vlazy

懒加载定义

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

指令

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){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){
        var 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://tva1.sinaimg.cn/large/007S8ZIlgy1gfyof9vr4mj3044032dfl.jpg'});

在组件中使用

<img v-lazy="https://tva1.sinaimg.cn/large/007S8ZIlgy1gfynwi1sejj30ij0nrdx0.jpg" />

最终效果

代码地址

https://github.com/erdong0604/demo/tree/master/vue-demo/v-lazy

退出移动版