Vue插件开辟初体验——(懒加载)

Vue插件开辟初体验——(懒加载)

媒介

闲来无事,想本身开辟一个简朴的Vue懒加载插件,才能的提拔我以为是可以经由历程编写插件完成,研讨了一下官网的
Vue插件编写。立时本身自力最先编写懒加载插件。

一、写在前面

由于我在网上看了很多关于vue插件的实例,发明险些都没有什么细致的教程,本身琢磨了半天也没有什么提高。都是写的比较精简。终究狠下心来,我们来本身憋一个插件出来吧w(゚Д゚)w!此次我们经由历程一个经常运用插件——懒加载,来体验一下vue的插件开辟。

萌新小白,前端开辟入门一年不到,迎接交换,给我提出批评看法感谢!!

原创泉源我的博客 迎接交换,GitHub项目地点:vue-simple-lazyload上面是一切源码,以为对写插件有协助就点个star吧

二、前期预备

2.1 挑选适宜的打包东西

适宜的打包东西可以到达事半功倍的结果。一最先我的首选有两个,一个是webpack,一个是rollup。下面简朴引见一下我为何挑选了rollup。

尽人皆知,webpack是一个险些席卷了一切静态资本,可以动态按需加载的一个包东西。而rollup也是一个模块打包器,可以把一个大块庞杂的代码拆分红各个小模块。

深图远虑后,我以为webpack也可以打包,然则起首,有点“杀鸡焉用牛刀”的觉得。而我的这个懒加载插件则需要提供给他人运用,同时又要保证全部插件的“轻量性”(打包完也许6KB,而webpack则比较大),不喜欢像webpack那样在这插件上痴肥的表现。

关于非应用级的顺序,我比较倾向于运用rollup.js。

2.2 确认项目组织

|——package.json
|——config
|    |——rollup.config.js
|——dist
|    |——bundle.js
|——src
|    |——index.js
|    |——directive.js
|    |——mixin.js
|    |——imagebox.js
|    |——lazyload.js
|    |——utils
|    |    |——utils.js
|    |——cores
|        |——eventlistener.js

config文件夹下安排rollup的设置文件。src为源文件夹,cores下面的文件夹为主要的模块,utils为东西类,重假如一些可以通用的模块要领。也许的组织就是如许。

2.3 设想思绪

好的设想思绪是一个插件的魂魄,我以本身不在道上的设想才能,自创了很多大神的头脑!很不自信地设想了懒加载的插件项目组织,见下:

  • index.js:这个文件是进口总文件,主要为了暴露vue插件的install要领,供外部挪用。
  • directive.js:这个文件是vue指令的文件,懒加载主要运用到的就是指令,我们这里定义v-simple-lazy指令逻辑写到这个文件内。
  • mixin.js:我们的中心骨来了,这个夹杂(mixin)定义在vue中诠释得有点不清楚,多是我理解才能有题目吧(ಥ_ಥ)。我们这里简朴点说,夹杂就是vue的一些性子(比方双绑)依据它给的划定规矩,跟你本身的定义,可以把你定义的一些变量,混入到vue实例中,也就是说,当作vue的$vm实例的变量来用,同时具有了实例的一些特征。

上述都是关于vue插件的一些文件,下面我们来讲我们本身定义的一些:

  • imagebox.js(类):望文生义,这个就是一个图象的盒子(box),用来存储你定义懒加载的图片的一些预加载的地点和一些要领。我们这里设想5个数组用来寄存,离别是:item,itemAlready,itemPending,itemFailed,itemAll,离别作用是:用来寄存当前需要加载的图片地点和元素已加载完的图片地点和元素正在加载中的图片地点和元素加载失利的图片地点和元素一切绑定了指令的图片地点和元素。道理我们下面会说。
  • core的eventlistener.js(类):这个文件很主要,主要寄存当我们监听转动条的时刻,需要处置惩罚的一些逻辑。
  • lazyload.js:这个主要用来寄存一些稳定的量,比方加载图片的默许地点等等。

2.4 编写思绪和道理

道理以下:

经由历程监听转动条转动,来不停地遍历上述imagebox内里的item数组(这个数组寄存着需要懒加载的图片预加载地点),假如item内里有值,那末就举行图片的要求。举行要求的同时,我们把这个元素到场到itemPending内里去,假如加载完了就放到itemAlready内里,失利的放到failed内里,这就是基础的完成思绪。

懒加载的完成历程,我们这里先精简化。详细思绪以下:

=》把一切用指令绑定的元素增添数组初始化

=》监听转动条转动

=》推断元素是不是进入可视局限

=》假如进入可视局限,举行src预加载(存入缓存数组)

=》关于pending的图片,举行正在加载赋值,关于finsh完的图片,加载预加载src内里的值,关于error的图片,举行毛病图片src赋值

三、主要代码的编写

3.1 确认进口文件

Vue插件内里引见是如许的

MyPlugin.install = function (Vue, options) {
  // 1. 增添全局要领或属性
  Vue.myGlobalMethod = function () {
    // 逻辑...
  }

  // 2. 增添全局资本
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 逻辑...
    }
    ...
  })

  // 3. 注入组件
  Vue.mixin({
    created: function () {
      // 逻辑...
    }
    ...
  })

  // 4. 增添实例要领
  Vue.prototype.$myMethod = function (methodOptions) {
    // 逻辑...
  }
}

在表面暴露的要领就是install,运用的时刻直接Vue.use(“插件称号”)直接可以运用。我们在install要领内里填写关于指令(directive)和夹杂(mixin),然后对外公然这个要领,option没填写的话就是默许空对象。

夹杂重假如为了混入vue内部属性,是除了以上全局要领后又可以在全局运用的一种体式格局。

3.2 先编写设置文件

工欲善其事必先利其器,我们先编写rollup的设置代码(这里做了精简,庞杂的可以参照我的github顺序)。

rollup.config.js

import buble from 'rollup-plugin-buble';
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';

export default {
  input: 'src/index.js',//进口
  output: {
    file: 'dist/bundle.js',//输出的出口
    format: 'umd',//花样:相似的另有cjs,amd,iife等
  },
  moduleName: 'LazyLoad',//打包的模块称号,可以再Vue.use()要领运用
  plugins:[
      resolve(),
      commonjs(),//支撑commonJS
      buble(),
      babel({//关于ES6
          exclude: 'node_modules/**' // 只编译我们的源代码
      })
  ]
};

package.json

{
  "name": "lazyload",
  "version": "1.0.0",
  "description": "vue懒加载插件",
  "main": "index.js",
  "scripts": {
    "main": "rollup -c config/rollup.config.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "TangHy",
  "license": "MIT",
  "dependencies": {
    "path": "^0.12.7",
    "rollup": "^0.57.1"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.4",
    "babel-preset-env": "^1.6.1",
    "babel-preset-react": "^6.24.1",
    "rollup-plugin-babel": "^3.0.3",
    "rollup-plugin-buble": "^0.19.2",
    "rollup-plugin-commonjs": "^9.1.0",
    "rollup-plugin-node-resolve": "^3.3.0"
  }
}

注重个中的敕令:

rollup -c config/rollup.config.js

背面的config/…是途径,这里注重一下,我们只需要运转

npm run main

就可以举行打包了。

关于rollup不懂的处所或许运用,我有一篇博文也简朴引见了从零最先进修Rollup.js的前端打包利器

3.3 编写主顺序代码

3.3.1 directive.js

起首我们要先画好雏形,怎样将eventlistener文件和directive联络在一起?

第一步起首一定是援用eventlistener,同时由于它是一个类,我们这里只需指令每次inserted的时刻,我们都新new一个对象举行初始化,把能传的值都传过去。可以看到下面的操纵

import eventlistener from './cores/eventlistener'

var listener = null;

export default {

    inserted: function (el,binding, vnode, oldVnode) {
        var EventListener = new eventlistener(el,binding, vnode);//这里我们new一个新对象,把el(元素),binding(绑定的值),vnode(假造node)都传过去
        listener = EventListener;
        EventListener.init();//假设有一个init初始化函数
        EventListener.startListen();//这里初始化完举行监听
  },
    update: function(el,{name,value,oldValue,expression}, vnode, oldVnode){

    },
    unbind: function(){

    }
    
}

其次我们要斟酌在update钩子中,我们需要干什么?

有如许一种营业:当你一次性绑定完一切数据的时刻,假如这个图片已预加载完了,那末你再怎样转变这个指令绑定的值,都不可以完成革新图片了,所以我们在update更新新的图片地点是有必要的。同时解绑的时刻,作废绑定也是有必要的,那末继承往下写:

import eventlistener from './cores/eventlistener'

var listener = null;

export default {

    inserted: function (el,binding, vnode, oldVnode) {
        var EventListener = new eventlistener(el,binding, vnode);//这里我们new一个新对象,把el(元素),binding(绑定的值),vnode(假造node)都传过去
        listener = EventListener;
        EventListener.init();//假设有一个init初始化函数
        EventListener.startListen();//这里初始化完举行监听
  },
    update: function(el,{name,value,oldValue,expression}, vnode, oldVnode){
        if(value === oldValue){//没有变化就返回
            return;
        }
        listener.update(el,value);//有变化就举行更新(假设有update这个要领)
    },
    unbind: function(){
        listener.removeListen();//解绑移除监听
    }
    
}

先写生命周期中inserted的时刻绑定监听。到场的时刻new一个监听对象保留一切包含一切dom的。我们继承往下看。

3.4 中心代码

3.4.1 imagebox.js类

起首先把之前说到的几个数组都初始化了。那末作为图象盒子(imagebox)的对象实例,我们需要哪些要领或属性呢?

起首初始化的时刻的add要领一定要,需要推断一下是不是有这个元素,没有的话就到场到item内里去。同时相似的另有addFailed,addPending等要领。

假如item中的元素加载完了,那末随之而来的就需要删除item中的元素,那末对应的remove要领也是必需要的,同时相似的另有removePending等要领。

export default class ImageBox {
  constructor() {
    this.eleAll = [];
    this.item = [];
    this.itemAlready = [];
    this.itemPending = [];
    this.itemFailed = [];
  }

  add(ele,src) {//insert插进去的时刻把一切的dom到场到数组中去初始化
      const index = this.itemAlready.findIndex((_item)=>{
          return _item.ele === ele;
      })
      if(index === -1){
          this.item.push({
            ele:ele,
            src:src
        })
      }
  }
    
  addPending(ele,src){
      this._addPending(ele,src);
      this._remove(ele);
  }
}

上述是一个图片的box,用于存取页面加载时刻,image图片对象的box存取。主要思绪是分了三个数组,一个存储一切的图片一个存储正在加载的图片一个存储加载失利的图片,然后最主要的是!!!

把这个imagebox要混入到全局,使其可以当作全局变量在全局运用

mixin.js
import imagebox from './imagebox'

const mixin = {
    data () {
      return {
          imagebox: new imagebox()//这里声明一个new对象,存在全局的变量中,混入vue内部,可以全局运用
      }
  }
}

export default mixin;

下所示代码中:

  1. 组织器中初始化种种元素数组,包含一切图片,已加载的图片,正在要求的图片,失利的图片等等
  2. add要领是在item数组中增添图片元素
  3. addPending是在正在要求的图片数组中增添元素的要领,相似的另有addFailed,addAlready,_remove等私有和共有要领
  4. util是种种东西类要领,包含推断图片是不是进入视野

依据上述思绪,完成以下代码:

(补充:这里有个update要领,思绪是更新了后,举行一切的数组遍历,找到相对应的元素,然后举行src就是其值的更新)

export default class ImageBox {
  constructor() {
    this.eleAll = [];
    this.item = [];
    this.itemAlready = [];
    this.itemPending = [];
    this.itemFailed = [];
  }

  add(ele,src) {
      const index = this.itemAlready.findIndex((_item)=>{
          return _item.ele === ele;
      })
      if(index === -1){
          this.item.push({
            ele:ele,
            src:src
        })
      }
  }

  update(ele,src){
    let index = this.itemAlready.findIndex(item=>{
      return item.ele === ele;
    });

    if(index != -1){
      this.itemAlready.splice(index,1);
      this.add(ele,src);
      return;
    };

    let _index = this.itemFailed.findIndex(item=>{
      return item.ele === ele;
    });

    if(_index !=-1){
      this.itemFailed.splice(_index,1);
      this.add(ele,src);
      return;
    };

  }

  addFailed(ele,src){
      this._addFailed(ele,src);
      this._removeFromPending(ele);
  }

  addPending(ele,src){
      this._addPending(ele,src);
      this._remove(ele);
  }

  addAlready(ele,src){
      this._addAlready(ele,src);
      this._removeFromPending(ele);
  }

  _addAlready(ele,src) {
      const index = this.itemAlready.findIndex((_item)=>{
          return _item.ele === ele;
      })
      if(index === -1){
          this.itemAlready.push({
            ele:ele,
            src:src
        })
      }
  }

  _addPending(ele,src) {
      const index = this.itemPending.findIndex((_item)=>{
          return _item.ele === ele;
      })
      if(index === -1){
          this.itemPending.push({
            ele:ele,
            src:src
        })
      }
  }

  _addFailed(ele,src) {
      const index = this.itemFailed.findIndex((_item)=>{
          return _item.ele === ele;
      })
      if(index === -1){
          this.itemFailed.push({
            ele:ele,
            src:src
        })
      }
  }

  _remove(ele) {
      const index = this.item.findIndex((_item)=>{
          return _item.ele === ele;
      });
      if(index!=-1){
          this.item.splice(index,1);
      }
  }

  _removeFromPending(ele) {
      const index = this.itemPending.findIndex((_item)=>{
          return _item.ele === ele;
      });
      if(index!=-1){
          this.itemPending.splice(index,1);
      }
  }
}
3.4.2 utils.js
const isSeen = function(item,imagebox){
  var ele = item.ele;
  var src = item.src;
  //图片间隔页面顶部的间隔
  var top = ele.getBoundingClientRect().top;
  //页面可视地区的高度
  var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
  //top + 10 已进入了可视地区10像素
  if(top + 10 < windowHeight){
      return true;
  }else{
      return false;
  }
}

export {
  isSeen
};
3.4.3 eventlistener.js

这个文件重假如监听的一些逻辑,那末一定需要一些对象实例的属性。起首el元素一定需要,binding,vnode,$vm一建都先写进来。

其次imagebox一定也需要,是图片的对象实例。

init的要领这里和imagebox中的add要领联络起来,init一个就到场imagebox中的item一个新的元素。

startListen要领是用于在监听后举行逻辑操纵。

import {isSeen} from '../utils/utils'//引入东西类的内里的是不是看得见元素这个要领推断

export default class EventListener {
  constructor(el,binding,vnode) {
    this.el = el;//初始化种种需要的属性
    this.binding = binding;
    this.vnode = vnode;
    this.imagebox = null;
    this.$vm = vnode.context;
    this.$lazyload = vnode.context.$lazyload//夹杂mixin进去的选项
  }

  init(){
      if(!typeof this.binding.value === 'string'){
      throw new Error("您的图片源不是String范例,请重试");
      return;
    }
      this.imagebox = this.vnode.context.imagebox;
    this.imagebox.add(this.el,this.binding.value);//每有一个item,就往box中增添一个新的元素
    this.listenProcess();
  }

  startListen(){
      const _self = this;
      document.addEventListener('scroll',(e)=>{
          _self.listenProcess(e);//这里最先操纵
      })
  }
}

上面主要初始化了很多属性,包含vue的假造dom和种种包含el元素dom,binding指令传过来的值等等初始化。

此文件主要为了处置惩罚监听页面转动的,监听是不是图片进入到可视局限内,然后举行一系列下方的种种操纵。

依据image.onload,image.onerror要领举行图片预加载的逻辑操纵,假如看得见这个图片,那末就举行图片的加载(同时到场到pending内里去),加载完举行推断。

以下是process的函数listenProcess

const _self = this;
if(this.imagebox.item.length == 0){
    return;
};
this.imagebox.item.forEach((item)=>{
    if(isSeen(item)){//这里推断元素是不是看得见
        var image = new Image();//这里在赋值src前new一个image对象举行缓存,缓冲一下,可以做后续的加载或失利的函数处置惩罚
        image.src = item.src;
        _self._imageStyle(item);//转变item的款式

        _self.imagebox.addPending(item.ele,item.src);//在对象imagebox中到场了正在pending要求的item(后续会引见imagebox类)

        image.onload = function(){//加载胜利的处置惩罚
            if(image.complete){
                _self.imageOnload(item);
            }
        }

        image.onerror = function(){//加载失利的处置惩罚
            _self.imageOnerror(item);
        }
    }
})

另有其他的一些要领:

imageOnload(item){//图片加载完的操纵
      this._removeImageStyle(item.ele);
    this.imagebox.addAlready(item.ele,item.src);//增添到已加载完的item数组内里
    this._imageSet(item.ele,item.src)
  }

  imageOnerror(item){//涌现毛病的时刻
      this._removeImageStyle(item.ele);
    this.imagebox.addFailed(item.ele,item.src);//增添到涌现毛病item数组内里
    this._imageSet(item.ele,this.$lazyload.options.errorUrl)//把设置中的毛病图片url填入
  }

  _imageStyle(item){
    item.ele.style.background = `url(${this.$lazyload.options.loadUrl}) no-repeat center`;
  }

  _removeImageStyle(ele){
      ele.style.background = '';
  }

  _imageSet(ele,value){//关于图片赋值src的操纵
    ele.src = value;
  }

补充一个update要领:

update(ele,src){
    console.log("更新了");
    console.log(this.imagebox);
    this.imagebox.update(ele,src);//挪用imagebox中的update要领
    this.listenProcess();//再举行是不是看得见的process操纵
}

下面是一切的eventlistener.js代码:

import {isSeen} from '../utils/utils'

export default class EventListener {
  constructor(el,binding,vnode) {
    this.el = el;
    this.binding = binding;
    this.vnode = vnode;
    this.imagebox = null;
    this.$vm = vnode.context;
    this.$lazyload = vnode.context.$lazyload
  }
  //绑定初始化
  init(){
    if(!typeof this.binding.value === 'string'){
      throw new Error("您的图片源不是String范例,请重试");
      return;
    }
      this.imagebox = this.vnode.context.imagebox;
    this.imagebox.add(this.el,this.binding.value);
    this.listenProcess();
  }
  //最先监听
  startListen(){
    var listenProcess = this.listenProcess;
      document.addEventListener('scroll',listenProcess.bind(this),false);
  }
  //移除监听
  removeListen(){
    var listenProcess = this.listenProcess;
    document.removeEventListener('scroll',listenProcess.bind(this),false);
  }
  //监听的操纵函数,包含推断image的box举行要求等
  listenProcess(){
      const _self = this;
    if(this.imagebox.item.length == 0){
      return;
    };

      this.imagebox.item.forEach((item)=>{
            if(isSeen(item)){
          var image = new Image();
          image.src = item.src;
          _self._imageStyle(item);
          
          _self.imagebox.addPending(item.ele,item.src);

          image.onload = function(){
            if(image.complete){
                _self.imageOnload(item);
            }
          }

          image.onerror = function(){
            _self.imageOnerror(item);
          }
            }
        })
  }
  //举行最新图片地点的更新
  update(ele,src){
    console.log("更新了");
    console.log(this.imagebox);
    this.imagebox.update(ele,src);
    this.listenProcess();
  }
  //详细得图片加载的操纵
  imageOnload(item){
      this._removeImageStyle(item.ele);
    this.imagebox.addAlready(item.ele,item.src);
    this._imageSet(item.ele,item.src)
  }
  //图片加载毛病的操纵
  imageOnerror(item){
      this._removeImageStyle(item.ele);
    this.imagebox.addFailed(item.ele,item.src);
    this._imageSet(item.ele,this.$lazyload.options.errorUrl)
  }
  //加载图片地点的赋值
  _imageStyle(item){
    item.ele.style.background = `url(${this.$lazyload.options.loadUrl}) no-repeat center`;
  }
  //移除加载图片的地点
  _removeImageStyle(ele){
      ele.style.background = '';
  }
  //对图片举行赋值
  _imageSet(ele,value){
    ele.src = value;
  }
}

一切的诠释都已写在上面的代码块内里了。

3.4.4 lazyload.js

末了把一些加载的图片默许设置或许失利图片地点完成以下代码:

const DEFAULT_ERROR_URL = './404.svg';
const DEFAULT_LOAD_URL = './loading-spin.svg';

export default class LazyLoad {
    constructor() {
    this.options = {
        loadUrl: DEFAULT_LOAD_URL,
        errorUrl: DEFAULT_ERROR_URL
    };
  }

  register(options){
      Object.assign(this.options, options);
  }
} 

此类临时用来存储种种设置和lazy的预定默许值,options内里存加载的时刻的图片地点和毛病加载的时刻的图片地点。

默许值是最上面两个值,是不传数据默许的设置。

3.4.5 index.js
import directive from './directive';
import mixin from './mixin';
import lazyload from './lazyload';

const install = ( Vue,options = {} )=>{
    const lazy = new lazyload();
    lazy.register(options);

    Vue.prototype.$lazyload = lazy

    Vue.mixin(mixin);
    
    Vue.directive('simple-lazy',directive);

}

export default {
    install
};

把上述一切的举行一个综合,放在这个进口文件举行向外暴露。

index就是全部项目的进口文件,至此我们完成了懒加载插件的基础代码编写。

四、打包成.js可以外部直接援用

打包后的代码:

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global.LazyLoad = factory());
}(this, (function () { 'use strict';

  var isSeen = function isSeen(item, imagebox) {
    var ele = item.ele;
    var src = item.src;
    //图片间隔页面顶部的间隔
    var top = ele.getBoundingClientRect().top;
    //页面可视地区的高度
    var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
    //top + 10 已进入了可视地区10像素
    if (top + 10 < windowHeight) {
      return true;
    } else {
      return false;
    }
  };

  var EventListener = function EventListener(el, binding, vnode) {
    this.el = el;
    this.binding = binding;
    this.vnode = vnode;
    this.imagebox = null;
    this.$vm = vnode.context;
    this.$lazyload = vnode.context.$lazyload;
  };

  EventListener.prototype.init = function init() {
    this.imagebox = this.vnode.context.imagebox;
    this.imagebox.add(this.el, this.binding.value);
    this.listenProcess();
  };

  EventListener.prototype.startListen = function startListen() {
    var listenProcess = this.listenProcess;
    window.addEventListener('scroll', listenProcess.bind(this), false);
  };

  EventListener.prototype.removeListen = function removeListen() {
    var listenProcess = this.listenProcess;
    window.removeEventListener('scroll', listenProcess.bind(this), false);
  };

  EventListener.prototype.listenProcess = function listenProcess() {
    var _self = this;
    if (this.imagebox.item.length == 0) {
      return;
    }
    this.imagebox.item.forEach(function (item) {
      if (isSeen(item)) {

        var image = new Image();
        image.src = item.src;
        _self._imageStyle(item);

        _self.imagebox.addPending(item.ele, item.src);

        image.onload = function () {
          if (image.complete) {
            _self.imageOnload(item);
          }
        };

        image.onerror = function () {
          _self.imageOnerror(item);
        };
      }
    });
  };

  EventListener.prototype.update = function update(ele, src) {
    console.log("更新了");
    console.log(this.imagebox);
    this.imagebox.update(ele, src);
    this.listenProcess();
  };

  EventListener.prototype.imageOnload = function imageOnload(item) {
    this._removeImageStyle(item.ele);
    this.imagebox.addAlready(item.ele, item.src);
    this._imageSet(item.ele, item.src);
  };

  EventListener.prototype.imageOnerror = function imageOnerror(item) {
    this._removeImageStyle(item.ele);
    this.imagebox.addFailed(item.ele, item.src);
    this._imageSet(item.ele, this.$lazyload.options.errorUrl);
  };

  EventListener.prototype._imageStyle = function _imageStyle(item) {
    item.ele.style.background = "url(" + this.$lazyload.options.loadUrl + ") no-repeat center";
  };

  EventListener.prototype._removeImageStyle = function _removeImageStyle(ele) {
    ele.style.background = '';
  };

  EventListener.prototype._imageSet = function _imageSet(ele, value) {
    ele.src = value;
  };

  var listener = null;

  var directive = {

      inserted: function inserted(el, binding, vnode, oldVnode) {
          var EventListener$$1 = new EventListener(el, binding, vnode);
          listener = EventListener$$1;
          EventListener$$1.init();
          EventListener$$1.startListen();
      },
      update: function update(el, ref, vnode, oldVnode) {
          var name = ref.name;
          var value = ref.value;
          var oldValue = ref.oldValue;
          var expression = ref.expression;

          if (value === oldValue) {
              return;
          }
          listener.update(el, value);
      },
      unbind: function unbind() {
          listener.removeListen();
      }

  };

  var ImageBox = function ImageBox() {
    this.eleAll = [];
    this.item = [];
    this.itemAlready = [];
    this.itemPending = [];
    this.itemFailed = [];
  };

  ImageBox.prototype.add = function add(ele, src) {
    var index = this.itemAlready.findIndex(function (_item) {
      return _item.ele === ele;
    });
    if (index === -1) {
      this.item.push({
        ele: ele,
        src: src
      });
    }
  };

  ImageBox.prototype.update = function update(ele, src) {
    var index = this.itemAlready.findIndex(function (item) {
      return item.ele === ele;
    });

    if (index != -1) {
      this.itemAlready.splice(index, 1);
      this.add(ele, src);
      return;
    }
    var _index = this.itemFailed.findIndex(function (item) {
      return item.ele === ele;
    });

    if (_index != -1) {
      this.itemFailed.splice(_index, 1);
      this.add(ele, src);
      return;
    }};

  ImageBox.prototype.addFailed = function addFailed(ele, src) {
    this._addFailed(ele, src);
    this._removeFromPending(ele);
  };

  ImageBox.prototype.addPending = function addPending(ele, src) {
    this._addPending(ele, src);
    this._remove(ele);
  };

  ImageBox.prototype.addAlready = function addAlready(ele, src) {
    this._addAlready(ele, src);
    this._removeFromPending(ele);
  };

  ImageBox.prototype._addAlready = function _addAlready(ele, src) {
    var index = this.itemAlready.findIndex(function (_item) {
      return _item.ele === ele;
    });
    if (index === -1) {
      this.itemAlready.push({
        ele: ele,
        src: src
      });
    }
  };

  ImageBox.prototype._addPending = function _addPending(ele, src) {
    var index = this.itemPending.findIndex(function (_item) {
      return _item.ele === ele;
    });
    if (index === -1) {
      this.itemPending.push({
        ele: ele,
        src: src
      });
    }
  };

  ImageBox.prototype._addFailed = function _addFailed(ele, src) {
    var index = this.itemFailed.findIndex(function (_item) {
      return _item.ele === ele;
    });
    if (index === -1) {
      this.itemFailed.push({
        ele: ele,
        src: src
      });
    }
  };

  ImageBox.prototype._remove = function _remove(ele) {
    var index = this.item.findIndex(function (_item) {
      return _item.ele === ele;
    });
    if (index != -1) {
      this.item.splice(index, 1);
    }
  };

  ImageBox.prototype._removeFromPending = function _removeFromPending(ele) {
    var index = this.itemPending.findIndex(function (_item) {
      return _item.ele === ele;
    });
    if (index != -1) {
      this.itemPending.splice(index, 1);
    }
  };

  var mixin = {
      data: function data() {
          return {
              imagebox: new ImageBox()
          };
      }
  };

  var DEFAULT_ERROR_URL = './404.svg';
  var DEFAULT_LOAD_URL = './loading-spin.svg';

  var LazyLoad = function LazyLoad() {
    this.options = {
      loadUrl: DEFAULT_LOAD_URL,
      errorUrl: DEFAULT_ERROR_URL
    };
  };

  LazyLoad.prototype.register = function register(options) {
    Object.assign(this.options, options);
  };

  var install = function install(Vue, options) {
      if (options === void 0) options = {};

      var lazy = new LazyLoad();
      lazy.register(options);

      Vue.prototype.$lazyload = lazy;

      Vue.mixin(mixin);

      Vue.directive('simple-lazy', directive);
  };

  var index = {
      install: install
  };

  return index;

})));

运用要领

Vue.use(LazyLoad,{
    loadUrl:'./loading-spin.svg',//这里写你的加载时刻的图片设置
    errorUrl:'./404.svg'//毛病加载的图片设置
});

元素中运用指令

<img v-simple-lazy="item" v-for="(item,$key) in imageArr">

imageArr测试数据

imageArr:[
    'http://covteam.u.qiniudn.com/test16.jpg?imageView2/2/format/webp',
    'http://covteam.u.qiniudn.com/test14.jpg?imageView2/2/format/webp',
    'http://covteam.u.qiniudn.com/test15.jpg?imageView2/2/format/webp',
    'http://covteam.u.qiniudn.com/test17.jpg?imageView2/2/format/webp',
    'http://hilongjw.github.io/vue-lazyload/dist/test9.jpg',
    'http://hilongjw.github.io/vue-lazyload/dist/test10.jpg',
    'http://hilongjw.github.io/vue-lazyload/dist/test14.jpg'
]

测试地点:戳我戳我

五、跋文

实在这些代码的编写照样比较简朴的,写完事后举行总结,你会发明,个中最难的是:

全部项目的组织,和代码模块之间的逻辑关系

这个才是最难控制的,假如涉及到大一点的项目,好一点的项目组织能让全部项目进度等等要素发作庞大的变化,提拔庞大的效力。而写插件最难的就是在这。

怎样有效地拆分代码?怎样有效地举行项目组织的组织? 这才是全部插件编写的中心。

之前写过一个vue关于表单考证的插件,也是被项目组织搞得焦头烂额,这里把简朴的懒加载基础代码做一个总结。写这个纯粹是个人兴趣。愿望可以给入门的插件开辟新人赋予一点点协助。

所以我深知写插件的时刻,它组织和模块化的主要性。而组织和模块化的优异,会让你事半功倍。别的迎接人人来我的博客观光唐益达的博客,只写原创。

萌新小白,前端开辟入门一年不到,迎接交换!

    原文作者:Leon
    原文地址: https://segmentfault.com/a/1190000015645724
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞