Vue是可以自定义指令的,最近学习过程中遇见了一个需要图片懒加载的功能,最后参考了别人的代码和思路自己重新写了一遍。以下将详细介绍如何实现自定义指令v-lazyload。
先看如何使用这个指令:
<img v-lazyload="imageSrc" >
imageSrc是要加载的图片的实际路径。
为了实现这个指令,我们首先单独建立一个文件,名字为lazyload.js.并填写基本的代码,如下:
//Vue 图片懒加载,导出模块 export default (Vue , options = {})=>{ //初始化的选项,default是未加载图片时显示的默认图片 var init = { default: 'https://gw.alicdn.com/tps/i1/TB147JCLFXXXXc1XVXXxGsw1VXX-112-168.png' } //addListener为Vue指令的具体实现功能函数,我们这里为所有使用v-lazyload的指令的元素添加监听 //ele 是dom元素,binding是绑定的具体值, //例如:<img v-lazyload="imageSrc" > ele是img binding是imageSrc const addListenner = (ele,binding) =>{ } //Vue自定义指令,lazyload为指令的名称 Vue.directive('lazyload',{ inserted:addListener, updated:addListener }) }
inserted 和 updated为Vue指令的执行不同阶段提供的钩子函数,查看Vue的官网可以看到一共有5个阶段,
指令定义函数提供了几个钩子函数(可选):
bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。
componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
unbind: 只调用一次, 指令与元素解绑时调用。
这里我们只用inserted和updated就够了。
接下来我们具体实现addListener的实现。我们的具体思路如下:
1、先看看这个图片是否需要懒加载。有两种情况,一是图片还没到达可视区域,二是图片已经加载过了。
2、然后监听窗口的scroll事件,判断哪些图片可以进行懒加载了。
这里我们需要一个需要进行监听需要懒加载的图片列表和一个需要记录已经加载过得图片列表。另外为了方便数组的操作,我们加一个数组的remove方法。
继续我们的代码。
//Vue 图片懒加载 export default (Vue , options = {})=>{ //数组item remove方法 if(!Array.prototype.remove){ Array.prototype.remove = function(item){ if(!this.length) return var index = this.indexOf(item); if( index > -1){ this.splice(index,1); return this } } } var init = { default: 'https://gw.alicdn.com/tps/i1/TB147JCLFXXXXc1XVXXxGsw1VXX-112-168.png' } //需要进行监听的图片列表,还没有加载过得 var listenList = []; //已经加载过得图片缓存列表 var imageCatcheList = []; //是否已经加载过了 const isAlredyLoad = (imageSrc) => { } //检测图片是否可以加载,如果可以则进行加载 const isCanShow = (item) =>{ }; //添加监听事件scroll const onListenScroll = () =>{ } //Vue 指令最终的方法 const addListener = (ele,binding) =>{ //绑定的图片地址 var imageSrc = binding.value; //如果已经加载过,则无需重新加载,直接将src赋值 if(isAlredyLoad(imageSrc)){ ele.src = imageSrc; return false; } var item = { ele:ele, src:imageSrc } //图片显示默认的图片 ele.src = init.default; //再看看是否可以显示此图片 if(isCanShow(item)){ return } //否则将图片地址和元素均放入监听的lisenList里 listenList.push(item); //然后开始监听页面scroll事件 onListenScroll(); } Vue.directive('lazyload',{ inserted:addListener, updated:addListener }) }
接下来就几个空方法的实现了。
isAlredyLoad ,判断是否已经加载过了这个图片
const isAlredyLoad = (imageSrc) => { if(imageCatcheList.indexOf(imageSrc) > -1){ return true; }else{ return false; } }
isCanShow 图片是否进入可视区域,如果已经进入则进行加载
//检测图片是否可以加载,如果可以则进行加载 const isCanShow = (item) =>{ var ele = item.ele; var src = item.src; //图片距离页面顶部的距离 var top = ele.getBoundingClientRect().top; //页面可视区域的高度 var windowHeight = window.innerHight; //top + 10 已经进入了可视区域10像素 if(top + 10 < window.innerHeight){ var image = new Image(); image.src = src; image.onload = function(){ ele.src = src; imageCatcheList.push(src); listenList.remove(item); } return true; }else{ return false; } };
onListenScroll监听滚动事件,并且检测是否进入可视区域。
const onListenScroll = () =>{ window.addEventListener('scroll',function(){ var length = listenList.length; for(let i = 0;i<length;i++ ){ isCanShow(listenList[i]); } }) }
最终我们的代码如下: