Vue是能够自定义指令的,最近学习过程当中碰见了一个须要图片懒加载的功能,最后参考了别人的代码和思路本身从新写了一遍。如下将详细介绍如何实现自定义指令v-lazyload。vue
先看如何使用这个指令:数组
<img v-lazyload="imageSrc" >复制代码
imageSrc是要加载的图片的实际路径。缓存
为了实现这个指令,咱们首先单独创建一个文件,名字为lazyload.js.并填写基本的代码,以下:bash
//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个阶段,dom
指令定义函数提供了几个钩子函数(可选):函数
bind
: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数能够定义一个在绑定时执行一次的初始化动做。学习
inserted
: 被绑定元素插入父节点时调用(父节点存在便可调用,没必要存在于 document 中)。ui
update
: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。经过比较更新先后的绑定值,能够忽略没必要要的模板更新(详细的钩子函数参数见下)。this
componentUpdated
: 被绑定元素所在模板完成一次更新周期时调用。spa
unbind
: 只调用一次, 指令与元素解绑时调用。
这里咱们只用inserted和updated就够了。
接下来咱们具体实现addListener的实现。咱们的具体思路以下:
一、先看看这个图片是否须要懒加载。有两种状况是不须要加载的,一是图片还没到达可视区域,二是图片已经加载过了。
二、而后监听窗口的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]);
}
})
}复制代码
最终咱们的代码以下:
//Vue 图片懒加载
export default (Vue , options = {})=>{
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 = {
lazyLoad: false,
default: 'https://gw.alicdn.com/tps/i1/TB147JCLFXXXXc1XVXXxGsw1VXX-112-168.png'
}
var listenList = [];
var imageCatcheList = [];
const isAlredyLoad = (imageSrc) => {
if(imageCatcheList.indexOf(imageSrc) > -1){
return true;
}else{
return false;
}
}
//检测图片是否能够加载,若是能够则进行加载
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){ //obj.right-100 < windowWidth
var image = new Image();
image.src = src;
image.onload = function(){
ele.src = src;
imageCatcheList.push(src);
listenList.remove(item);
}
return true;
}else{
return false;
}
};
const onListenScroll = () =>{
window.addEventListener('scroll',function(){ //最好兼容左右的滑动touchend
var length = listenList.length;
for(let i = 0;i<length;i++ ){
isCanShow(listenList[i]);
}
})
}
//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
})
}复制代码
使用时须要在主文件中引入这个文件,而且vue.use();
import LazyLoad from 'lazyLoad.js'
Vue.use(LazyLoad);复制代码
而且在须要懒加载的图片上均按照以下使用v-lazyload指令便可
<img v-lazyload="imageSrc" >复制代码