從無到有--手把手搭建一個(gè)通用的jQuery插件模板

眾所周知,微信自從提出開發(fā)小程序后,就成為了一個(gè)可以比肩Goggle、Apple的具有平臺(tái)性質(zhì)的超級(jí)app。同樣的,jQuery最成功的地方莫過于:它的可擴(kuò)展性吸引了眾多開發(fā)者為其開發(fā)插件,從而建立起了一個(gè)生態(tài)系統(tǒng)。

學(xué)會(huì)使用jQuery并不難,因?yàn)樗?jiǎn)單易學(xué),我們?cè)谑褂胘Query時(shí)肯定使用或熟悉了不少其插件。如果我們希望提升自己的編程水平,編寫一個(gè)屬于自己的jQuery插件是個(gè)不錯(cuò)的選擇。

jQuery插件的開發(fā)模式

軟件開發(fā)過程中是需要一定的設(shè)計(jì)模式來指導(dǎo)開發(fā)的,有了模式,我們就能更好地組織我們的代碼,并且從這些前人總結(jié)出來的模式中學(xué)到很多好的實(shí)踐。

根據(jù)《jQuery高級(jí)編程》的描述,jQuery插件開發(fā)方式主要有三種:

1.通過$.extend()來擴(kuò)展jQuery

2.通過$.fn 向jQuery添加新的方法

3.通過$.widget()應(yīng)用jQuery UI的部件工廠方式創(chuàng)建

第三種方式是用來開發(fā)更高級(jí)jQuery部件的,該模式開發(fā)出來的部件帶有很多jQuery內(nèi)建的特性,比如插件的狀態(tài)信息自動(dòng)保存,各種關(guān)于插件的常用方法等,本篇文章暫不涉及該開發(fā)模式。

再來比較第一種和第二種開發(fā)模式:

很顯然,第一種開發(fā)模式是直接在關(guān)鍵字$、jQuery上掛載相應(yīng)的方法,使jQuery具有靜態(tài)方法,也可以理解為使用$.extend()在jQuery的命名空間里直接擴(kuò)展對(duì)應(yīng)的全局方法。這種方法的聲明和使用都比較簡(jiǎn)單,示例代碼如下所示:

$.extend({

????Alert: function(message) {

????????alert('app:' + message);

????}

});

$.Alert('hello world!');

效果如下:


$.Alert('hello world!')

上述代碼標(biāo)識(shí):聲明一個(gè)自定義Alert方法,通過$.Alert('hello world!')調(diào)用,彈出"app:hello world"。

這種方式一般適用于一些輔助方法,比如上述重寫alert方法,或者在console.log()時(shí)加入打印的時(shí)間等。

但這種方式無法利用jQuery強(qiáng)大的選擇器帶來的便利,要處理DOM元素以及將插件更好地運(yùn)用于所選擇的元素身上,還是需要使用第二種開發(fā)方式。你所見到或使用的插件也大多是通過此種方式開發(fā)。

插件開發(fā)

基本方法

聲明:

$.fn.myPlugin= function() {

? ? //to do list

}

在$.fn上掛載一個(gè)方法,方法名就是我們的插件名,然后具體邏輯在這個(gè)方法內(nèi)展開。

調(diào)用

$('#app a').myPlugin();

例如:做一個(gè)插件,將頁面上所有的a標(biāo)簽的字體顏色改為'red',那么代碼如下:

//html

<div id="app">

? ? ? ? <a href="javascript:void(0);">谷歌</a>

? ??? ? <a>href="javascript:void(0);">百度</a>

? ? ? ? <a>href="javascript:void(0);">網(wǎng)易</a>

</div>

//javascript(需要引入jQuery)

$.fn.myPlugin = function() {

? ? //this的解釋及注意事項(xiàng)見下文

????this.css('color', 'red');

}

$('#app a').myPlugin();

注意:this代表的是當(dāng)前調(diào)用的對(duì)象,上述代碼調(diào)用myPlugin的是$('#app a'),那么this代表的就是$('#app a'),所以方法體內(nèi)的this.css('color', 'red')相當(dāng)于$('#app a').css('color', 'red'),同理,如果使用$('#app')那么this代表的就是$('#app')。那么就有同學(xué)問:為什么方法體內(nèi)部不直接使用$('#app a')呢?

答:書寫插件的目的就是為了最大限度的復(fù)用代碼,我們不僅希望可以將id="app"內(nèi)的a標(biāo)簽字體顏色全部改為'red',還希望可以將id="store"內(nèi)的a標(biāo)簽字體顏色全部改為'red',如果我們?cè)诜椒w內(nèi)將this改為$('#app a'),那么id="stroe"的方法是不是也要改為$('#store a').css('color', 'red')?這不就違背我們開發(fā)jQuery插件的初衷了嗎?

那么有人還會(huì)問,為什么直接使用this而不是使用$(this)?

答:在插件名字定義的這個(gè)函數(shù)內(nèi)部,this指代的是我們?cè)谡{(diào)用該插件時(shí),用jQuery選擇器選中的元素,一般是一個(gè)jQuery類型的集合。比如$('a')返回的是頁面上所有a標(biāo)簽的集合,且這個(gè)集合已經(jīng)是jQuery包裝類型了,也就是說,在對(duì)其進(jìn)行操作的時(shí)候可以直接調(diào)用jQuery的其他方法而不需要再用美元符號(hào)來包裝一下。

效果如下:

$('#app a').myPlugin();

支持鏈?zhǔn)秸{(diào)用

在jQuery官方文檔中,有以下類似的例子:

$('#d1').css('color', 'red').addClass('classA');

這段代碼表示:將id="d1"的元素的字體改為'red',同時(shí)添加一個(gè)'classA'的類。這是一個(gè)典型的"鏈?zhǔn)秸{(diào)用"的例子。那么jQuery在開發(fā)插件時(shí),我們也可以實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用。具體方法如下:

$.fn.myPlugin = function() {

? ? //this的解釋及注意事項(xiàng)見下文

? ? return this.css('color', 'red');

}

$('#app a').myPlugin().addClass('classA');


效果如下:


鏈?zhǔn)秸{(diào)用

如果插件內(nèi)不使用 return this,那么鏈?zhǔn)秸{(diào)用的時(shí)候雖然a標(biāo)簽的字體變成了red,但是在addClass的時(shí)候會(huì)報(bào)錯(cuò):


非鏈?zhǔn)秸{(diào)用

接收參數(shù)

在使用jQuery系列插件的時(shí)候,往往會(huì)有一個(gè)該插件的官網(wǎng)文檔,在這個(gè)文檔中會(huì)介紹相應(yīng)的參數(shù)配置,那么我們接下來學(xué)習(xí)書寫插件時(shí)如何傳遞參數(shù),以使我們插件更加健壯。

現(xiàn)在有一個(gè)需求:在調(diào)用插件時(shí),可以自定義字體顏色,如果在初始化的時(shí)候設(shè)定了自定義字體顏色,則設(shè)置為該顏色;如果未在初始化時(shí)自定義,則默認(rèn)顯示為紅色。

通常,我們聲明一個(gè)函數(shù),在使用函數(shù)的參數(shù)時(shí)是這樣的:

function sub(a, b, step) {

? ? return a + b + (typeof step === 'undefined' ? 0 : step);

}

這段代碼表示:在調(diào)用sub時(shí),如果參數(shù)step存在那么返回結(jié)果為a+b+step;反之,則返回a+b。

同樣,我們?cè)诓寮?nèi)也是可以這樣去聲明的:

$.fn.myPlugin(color) {

? ? return this.css('color', typeof color === 'undefined'? ? 'red' : color);

}

但是這僅僅是一個(gè)配置項(xiàng),如果我們的插件足夠大,配置項(xiàng)足夠多,那么這種在參數(shù)會(huì)羅列成color、href、width、 height、 bgImg等等,這樣非常不便于維護(hù)和升級(jí)。

那么為了避免參數(shù)越來越長(zhǎng),我們采用一個(gè)傳遞一個(gè)對(duì)象的方式:

$.fn.myPlugin(opts) {

????this.css('color', typeof opts.color === 'undefined'? ? 'red' : opts.color);

? ? this.attr('href', ?typeof opts.href=== 'undefined'? ? '#' : opts.href );

????return this;

}

這樣看起來好多了,看似解決了我們參數(shù)過多的問題。

但是,這樣寫有一個(gè)弊端:每逢用到一個(gè)參數(shù)我們都要通過一個(gè)三目運(yùn)算(當(dāng)然if else也可以)來判斷這個(gè)參數(shù)是不是在傳了進(jìn)來,這樣代碼顯得臃腫不堪。好在jQuery提供了一個(gè)$.extend()的方法(不清楚這個(gè)方法是干什么用的的同學(xué)請(qǐng)出門左拐,百度一下),將插件內(nèi)的默認(rèn)項(xiàng)defaults與自定義參數(shù)opts進(jìn)行合并。

$.fn.myPlugin = function(opts) {

? ? var defaults = {

? ? ? ? color: 'red',

? ? ? ? href: '#'

????};

? ? var settings = $.extend(defaults, opts);

? ? this.css('color', settings.color);

? ??this.attr('href', settings.href);

? ? return this;

}

$('#app a').myPlugin({ color: 'green' }).addClass('classA');

效果如下:


jQuery-plugin

我們發(fā)現(xiàn):字體顏色被換成"green",href屬性仍然是使用默認(rèn)值"#"。

到此,插件可以接收和處理參數(shù)后,就可以編寫出更健壯而靈活的插件了。若要編寫一個(gè)復(fù)雜的插件,代碼量會(huì)很大,如何組織代碼就成了一個(gè)需要面臨的問題,沒有一個(gè)好的方式來組織這些代碼,整體感覺會(huì)雜亂無章,同時(shí)也不好維護(hù),所以將插件的所有方法屬性包裝到一個(gè)對(duì)象上,用面向?qū)ο蟮乃季S來進(jìn)行開發(fā),無疑會(huì)使工作輕松很多。

面向?qū)ο蟮牟寮_發(fā)

為什么要通過面向?qū)ο筮M(jìn)行jQuery插件開發(fā)?核心原因還是為了便于維護(hù)。

例如:我們?cè)谝粋€(gè)項(xiàng)目中,會(huì)開發(fā)不止一個(gè)jQuery插件,那么如果采用$.fn.XXX的方法,就會(huì)出現(xiàn)N多個(gè)掛載方法,很容易出現(xiàn)命名沖突的情況。另外一個(gè)原因是,如果我們的所有屬性、方法都放在$.fn.XXX這個(gè)作用域里,那么我們這個(gè)插件會(huì)隨著功能的增加變得越來越臃腫,屬性、方法之間的耦合度會(huì)越來越大,那么后期的維護(hù)工作量之大可想而知;如若把屬性、方法放在插件作用域之外,那簡(jiǎn)直就是一個(gè)災(zāi)難----如何把控全局變量名和方法名?

面向?qū)ο蟮膉Query插件開發(fā)思想簡(jiǎn)單來講,就是在函數(shù)的作用域里將屬性、方法私有化,與外界進(jìn)行"隔離",使函數(shù)內(nèi)部的屬性、方法在"自己的圈子里"專注于"干自己的事情"。

以下代碼,我們通過一個(gè)具體的業(yè)務(wù)進(jìn)行開發(fā)navsilde,插件實(shí)現(xiàn)目的:

(1)配置導(dǎo)航data,插件自動(dòng)渲染html結(jié)構(gòu)。

(2)鼠標(biāo)放在某個(gè)導(dǎo)航選項(xiàng)上,下方的activeLine(一條藍(lán)色的線條)會(huì)向左或向右地滑動(dòng)到該選項(xiàng)下,鼠標(biāo)離開后會(huì)回復(fù)到active選項(xiàng)上。

(3)點(diǎn)擊某個(gè)選項(xiàng),那么會(huì)將當(dāng)前選項(xiàng)設(shè)置為active選項(xiàng)。

(4)插件提供一個(gè)方法,用來獲取配置的data。

下面我們由淺入深的講解面向?qū)ο蟮拈_發(fā)方法,最終實(shí)現(xiàn)這個(gè)插件。

首先,我們新建一個(gè)函數(shù),命名為Navslide,將Navslide綁定到$.fn上,代碼如下:

var? Navslide = function(element, options) {

? ? this.default = {

? ? ? ? data: [],

? ? ? ? active: null

????};

? ? this.element =? element;

? ? this.settings = $.extend({}, this.default,? options);

? ? this.init();

}

//插件入口函數(shù)

Navslide.prototype.init = function() {

? ? this.$nav = this.initNav();

}

//根據(jù)data渲染html

Navslide.prototype. initNav = function() {? ?

????var html = [],

????????datas = this.settings.data,

????????len = datas.length;

????html.push('< ul class="nav-slide-wrapper">');

????for (var i = 0; i < len; i++) {

????????html.push(

????????????'?<li class="nav-slide-item',

? ???????????????datas[i].isActive ? ' active':'',

? ? ? ? ? ?'">',

????????????????datas[i].text, '

? ? ? ? ? ?'</li>'

????????);

????}

????html.push('<i class="active-line"></i>');

????html.push('</ul>');

????return $(html.join('')).appendTo(this.element);

}

//在插件中調(diào)用對(duì)象

$.fn.navslide = function(options) {

? ? return new? Navslide(this, options );

}

//調(diào)用

$('#app'). navslide({

? ? data: [

?????????{'text': '新聞', 'value': 'xinwen', 'isActive': true},

? ???????{'text': '社會(huì)趣聞', 'value': 'shehuiquwen'},

? ?????? {'text': '視頻', 'value': 'shipin'},

? ?????? {'text': '新農(nóng)村', 'value': 'xinongcun'},

? ? ]

});

css代碼在本文省略,如有需要,請(qǐng)根據(jù)文末提供的Github地址下載完整代碼。

效果如下:


navslide

通過上面這樣一改造,我們的代碼變得更面向?qū)ο罅耍哺镁S護(hù)和理解,以后要加新功能新方法,只需向?qū)ο筇砑有伦兞考胺椒纯?,然后在插件里?shí)例化后即可調(diào)用新添加的東西。

命名空間

不僅僅是jQuery插件的開發(fā),我們?cè)趯懭魏蜫S代碼時(shí)都應(yīng)該注意的一點(diǎn)是不要污染全局命名空間。因?yàn)殡S著你代碼的增多,如果有意無意在全局范圍內(nèi)定義一些變量的話,最后很難維護(hù),也容易跟別人寫的代碼有沖突。

比如你在代碼中向全局window對(duì)象添加了一個(gè)變量status用于存放狀態(tài),同時(shí)頁面中引用了另一個(gè)別人寫的庫,也向全局添加了這樣一個(gè)同名變量,最后的結(jié)果肯定不是你想要的。所以不到萬不得已,一般我們不會(huì)將變量定義成全局的。

一個(gè)好的做法是始終用自調(diào)用匿名函數(shù)包裹你的代碼,這樣就可以完全放心,安全地將它用于任何地方了,絕對(duì)沒有沖突。還有一個(gè)好處就是,自調(diào)用匿名函數(shù)里面的代碼會(huì)在第一時(shí)間執(zhí)行,頁面準(zhǔn)備好過后,上面的代碼就將插件準(zhǔn)備好了,以方便在后面的代碼中使用插件。

;? ?//這里多個(gè)分號(hào)?看下邊的講解

(function() {

? ? var Navslide = function() {...}

? ? ...

? ? //這里的屬性名、方法名不會(huì)污染全局

})();

另外需要注意一個(gè)問題,在書寫javascript時(shí),并不強(qiáng)制需要分號(hào)結(jié)尾,我們?cè)谑褂米哉{(diào)用匿名函數(shù)時(shí)要注意格式問題。所以為了避免其他人的不規(guī)范代碼,我們?cè)陂_發(fā)插件時(shí),第一行首先使用一個(gè)分號(hào)強(qiáng)制結(jié)束其他語句,這樣我們的插件在初始化時(shí)就不會(huì)報(bào)錯(cuò)了!

命名空間傳參

細(xì)心的同學(xué)在閱讀一些優(yōu)秀的jQuery插件時(shí),往往會(huì)看到這樣的格式:

;

(function($, window, document, undefined) {

? ? //code here

}(jQuery, window, document));

自調(diào)用匿名函數(shù)傳了jQuery、window、documnet三個(gè)參數(shù),這是將系統(tǒng)變量傳入到函數(shù)內(nèi)部,我們?cè)趦?nèi)部調(diào)用的時(shí)候可以使用別名。

這里其實(shí)是有一個(gè)小知識(shí)點(diǎn):很多有同學(xué)認(rèn)為$代表的就是jQuery,其實(shí)不然,$只是jQuery的一個(gè)簡(jiǎn)寫,我們不僅可以把jQuery.js起一個(gè)別名叫$,同樣可以將Zepto.js起一個(gè)別用叫$。那么問題來了:當(dāng)我們不把jQuery傳入到自定義匿名函數(shù)中,而且我們項(xiàng)目組同時(shí)用到了jQuery.js和Zepto.js時(shí),函數(shù)內(nèi)部的$代表的是jQuery還是Zepto呢?當(dāng)然了,如果不怕麻煩,我們不需要傳入jQuery,將函數(shù)內(nèi)部使用到$的地方全部改為jQuery(selector)也是可以的,但是不提倡這樣做。

而至于這個(gè)undefined,比較有意思,為了得到?jīng)]有被修改的undefined,我們并沒有傳遞這個(gè)參數(shù),但卻在接收時(shí)接收了它,因?yàn)閷?shí)際并沒有傳,所以"undefined"那個(gè)位置接收到的就是真實(shí)的"undefined"了。有一點(diǎn)hack的感覺,也是從其他大??偨Y(jié)到的經(jīng)驗(yàn)。

最終模板


;

(function($, window, document, undefined) {

? ? ? ?var pluginName = 'navslide';

? ? ? ?var Navslide = function(element, option) {

? ? ? ? ? ? //初始化參數(shù)

????????????...

? ? ? ? ? ? //調(diào)用入口函數(shù)

? ? ? ? ? ? this.init();

? ? ? ?}

? ? ? //入口方法

? ???Navslide.prototype.init = function() {...}

? ? ?//業(yè)務(wù)方法

? ?? Navslide.prototype.XXX = function() {...}

? ? //綁定函數(shù)

? ? $.fn[pluginName] = function(options) {

? ? ? ? return new? Navslide(this, options);

????}

}(jQuery, window, document));

至此,我們搭建了一個(gè)通用的jQuery插件模板。由于篇幅有限,這個(gè)模板只能算是比較基礎(chǔ)的,在我們的實(shí)際業(yè)務(wù)中,還要進(jìn)行插件自定義方法、多語言、多主題、插件發(fā)布等設(shè)置,我會(huì)在之后的文章繼續(xù)推出。

滑動(dòng)導(dǎo)航條完整示例代碼:https://github.com/zhiyuanMain/jQuery-plugins/tree/master/how-to-init

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容