閒來無事,想本身開發一個簡單的Vue懶加載插件,能力的提高我以爲是能夠經過編寫插件實現,研究了一下官網的 Vue插件編寫。立刻本身獨立開始編寫懶加載插件。
因爲我在網上看了不少關於vue插件的實例,發現幾乎都沒有什麼詳細的教程,本身琢磨了半天也沒有什麼進步。都是寫的比較精簡。終於狠下心來,咱們來本身憋一個插件出來吧w(゚Д゚)w!此次咱們經過一個經常使用插件——懶加載,來體驗一下vue的插件開發。javascript
萌新小白,前端開發入門一年不到,歡迎交流,給我提出批評意見謝謝!!html
(原創來源個人博客 歡迎交流,GitHub項目地址:vue-simple-lazyload上面是全部源碼,以爲對寫插件有幫助就點個star吧)前端
合適的打包工具能夠達到事半功倍的效果。一開始個人首選有兩個,一個是webpack,一個是rollup。下面簡單介紹一下我爲何選擇了rollup。vue
衆所周知,webpack是一個幾乎囊括了全部靜態資源,能夠動態按需加載的一個包工具。而rollup也是一個模塊打包器,能夠把一個大塊複雜的代碼拆分紅各個小模塊。java
深思熟慮後,我以爲webpack也能夠打包,可是首先,有點「殺雞焉用牛刀」的感受。而個人這個懶加載插件則須要提供給別人使用,同時又要保證整個插件的「輕量性」(打包完大概6KB,而webpack則比較大),不喜歡像webpack那樣在這插件上臃腫的表現。node
對於非應用級的程序,我比較傾向於使用rollup.js。react
|——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爲工具類,主要是一些能夠通用的模塊方法。大概的結構就是這樣。webpack
好的設計思路是一個插件的靈魂,我以本身不在道上的設計能力,借鑑了許多大神的思想!很不自信地設計了懶加載的插件項目結構,見下:git
上述都是關於vue插件的一些文件,下面咱們來講咱們本身定義的一些:github
原理以下:
經過監聽滾動條滾動,來不停地遍歷上述imagebox裏面的item數組(這個數組存放着須要懶加載的圖片預加載地址),若是item裏面有值,那麼就進行圖片的請求。進行請求的同時,咱們把這個元素加入到itemPending裏面去,若是加載完了就放到itemAlready裏面,失敗的放到failed裏面,這就是基本的實現思路。
懶加載的實現過程,咱們這裏先精簡化。具體思路以下:
=》把全部用指令綁定的元素添加數組初始化
=》監聽滾動條滾動
=》判斷元素是否進入可視範圍
=》若是進入可視範圍,進行src預加載(存入緩存數組)
=》對於pending的圖片,進行正在加載賦值,對於finsh完的圖片,加載預加載src裏面的值,對於error的圖片,進行錯誤圖片src賦值
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內部屬性,是除了以上全局方法後又能夠在全局使用的一種方式。
工欲善其事必先利其器,咱們先編寫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的前端打包利器
首先咱們要先畫好雛形,如何將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的。咱們繼續往下看。
首先先把以前說到的幾個數組都初始化了。那麼做爲圖像盒子(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要混入到全局,使其能夠當作全局變量在全局使用。
import imagebox from './imagebox' const mixin = { data () { return { imagebox: new imagebox()//這裏聲明一個new對象,存在全局的變量中,混入vue內部,能夠全局使用 } } } export default mixin;
下所示代碼中:
根據上述思路,完成下列代碼:
(補充:這裏有個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); } } }
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 };
這個文件主要是監聽的一些邏輯,那麼確定須要一些對象實例的屬性。首先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; } }
全部的解釋都已經寫在上面的代碼塊裏面了。
最後把一些加載的圖片默認配置或者失敗圖片地址完成下列代碼:
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裏面存加載的時候的圖片地址和錯誤加載的時候的圖片地址。
默認值是最上面兩個值,是不傳數據默認的配置。
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就是整個項目的入口文件,至此咱們完成了懶加載插件的基本代碼編寫。
打包後的代碼:
(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:[ '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關於表單驗證的插件,也是被項目結構搞得焦頭爛額,這裏把簡單的懶加載基本代碼作一個總結。寫這個純粹是我的興趣。但願能夠給入門的插件開發新人給予一點點幫助。
因此我深知寫插件的時候,它結構和模塊化的重要性。而結構和模塊化的優秀,會讓你事半功倍。另外歡迎你們來個人博客參觀唐益達的博客,只寫原創。
萌新小白,前端開發入門一年不到,歡迎交流!