近期要準備一個web前端技術交流會的內容,所以陸續會有一些整理的資料發布,JQuery目前在組內日常開發中占據了重要地位,但各自為戰的情況 很明顯,要做到重用和通用,形成插件是不錯的辦法,特別是基于JQuery的插件,具有使用簡單,可參數化配置等優點。這篇文章就介紹了如何開發 JQuery的插件。原文地址是:http://www.learningjquery.com/2007/10/a-plugin-development-pattern

我已經開發基于JQuery的插件有一段時間了,對于各種形式和要求的插件開發有了較好的掌握。在這里,我將在本文中分享我認為十分有用的插件開發 方式。當前前提是假定你對JQuery的插件開發有一定了解,如果你是插件開發的新手,不妨先看看jQuery官網上的the jQuery Authoring Guidelines。

我認為以下插件開發模式是必須應該掌握的:

1.在JQuery命名空間內聲明一個特定的命名;
2.接收參數來控制插件的行為;
3.提供公有方法訪問插件的配置項值;
4.提供公有方法來訪問插件中其他的方法(如果可能的話);
5.保證私有方法是私有的;
6.支持元數據插件;

下面,我將逐一講述上面的內容,并在同時給出相關的簡單插件開發代碼。

1.在JQuery命名空間內聲明一個特定的命名

這意味著開發的是一個單一命名的插件腳本,如果你的腳本包含多個插件或者有補充性質的插件,比如$.fn.doSomething() 和$.fn.undoSomething(),那你得聲明多個命名了。但是總體來說,當開發一個插件時,我們應該努力做到用一個單一的命名來搞定整個插 件。

在例子中,我們將聲明一個名為“hilight”的插件。

  1. $. fn . hilight = function () {
  2. ? // Our plugin implementation code goes here.
  3. } ;?

我們可以這樣調用:
$(’#myDiv’).hilight();
但是假如我們需要打破這種單一的命名和調用方式呢?有很多理由支持我們這么做:設計上的需要;更加簡單和可讀的配置;而且那樣將更加符合OO的要求。

在沒有給命名空間來到麻煩的前提下,將插件的部署打破成為多個函數的形式將是十分繁瑣的。我們通過認識并利用JavaScript中 functions是最高層的對象,和其他對象一樣,functions可以被賦予屬性,前面我們已經將hilight命名聲明在了JQuery的原型對 象上,那么,其實,其他的我們想擴展的屬性或對象都能夠在hilight上進行聲明。稍后將詳細講述此點。

2.接收參數來控制插件的行為;

來為我們的hilight插件添加指定前景和背景色的功能,我們需要在函數中允許一個object類型的選項設置。如下所展示的那樣:

  1. $. fn . hilight = function ( options ) {
  2. ? var ? defaults = {
  3. ?? ? foreground : ' red ' ,
  4. ?? ? background : ' yellow '
  5. ?? } ;
  6. ?? // Extend our default options with those provided.
  7. ?? var ? opts = $. extend ( defaults , options ) ;
  8. ?? // Our plugin implementation code goes here.
  9. } ;

現在,我們的插件可以這樣來調用:

$(’#myDiv’).hilight({
??foreground: ‘blue’
});

3.提供公有方法訪問插件的配置項值;

上面的代碼我們可以做一下改進,使得插件的默認值可以在插件之外被設置。這無疑是十分重要的,因為它使得插件用戶可以使用最少的代碼來修改插件配置,這其實是我們利用函數對象的開始。

  1. // plugin definition
  2. $. fn . hilight = function ( options ) ? {
  3. ?? // Extend our default options with those provided.
  4. ?? // Note that the first arg to extend is an empty object -
  5. ?? // this is to keep from overriding our "defaults" object.
  6. ?? var ? opts = $. extend ({} , $. fn . hilight . defaults , options ) ;
  7. ?? // Our plugin implementation code goes here.
  8. } ;
  9. ?
  10. // plugin defaults - added as a property on our plugin function
  11. $. fn . hilight . defaults = {
  12. ?? foreground : ' red ' ,
  13. ?? background : ' yellow '
  14. } ;
4.提供公有方法來訪問插件中其他的方法(如果可能的話)

這里要講的方法和前面的講解一脈相承,用此方法來擴展你的插件(而且能夠讓其他人進行擴展)是件很有意思的事情。例如,在擴展hilight插件 時,我們可以定義一個format方法用來格式化高亮顯示的文本,原來的hilight插件和擴展了format方法的插件代碼如下:

  1. $. fn . hilight = function ( options ) {
  2. // iterate and reformat each matched element
  3. return ? this . each ( function () {
  4. var $ this = $ ( this ) ;
  5. ...
  6. var ? markup = $ this . html () ;
  7. // call our format function
  8. markup = $. fn . hilight . format ( markup ) ;
  9. $ this . html ( markup ) ;
  10. }) ;
  11. } ;
  12. ?
  13. // define our format function
  14. $. fn . hilight . format = function ( txt ) ? { '
  15. return ' < strong > ' + txt + ' < / strong>';
  16. };

如前面所述,我們已經很容易的通過設置options對象的屬性來允許一個回調函數來覆寫默認的格式設置。在這里有另外一個非常棒的方法來個性化你 的插件,上面展示的方法實際上就是通過暴露format方法,使其可以被重新定義。這種做法使得其他人可以采用他們自己的習慣和方式來重寫你的插件,這意 味著他們可以為你的插件寫額外的擴展插件。
仔細考量一下前面我們用到的插件例子程序,你可能會想“我們究竟應該在什么時候使用這種插件方式來實現需求”的問題。一個來自現實應用中的插件便是“ Cycle Plugin”,它是一個支持多種滑動顯示特效的插件,特效包括滾動、滑動和漸變等等。但是,實際上,并沒有辦法來定義每一個可能會用在滑動變幻上的特 效。這就是這種擴展方式的有用之處。“ Cycle Plugin”插件暴露了”transitions”對象,這使得用戶只需要按照如下方式便可以添加自己的變幻定義:
$.fn.cycle.transitions = {

};
這種技巧使得用戶可以定義或者采用自己習慣的方式來擴展“ Cycle Plugin”。

5.保證私有方法是私有的;

上面提到的暴露插件中的公有方法的技巧使得插件能夠被覆寫,這將使插件變得十分靈活而強大,但至于哪一部分,那些屬性和方法應該被暴露出來,你得小 心了。一旦使其能夠被外界訪問到,你就得注意到任何調用參數和語義化的變動都可能使其喪失向前的兼容性。作為一般準則,如果不確定是否應該暴露某個屬性或 對象的話,那就最好別那樣做。
那么我們應該怎樣來定義多個方法而不至于使命名空間混亂并且保證不被暴露再外呢?這就是閉包的工作,為了便于演示,我們給插件加入了一個叫做 “debug”的功能,它用來記錄firebug控制臺所選擇的網頁元素數目。為了創建一個閉包,我們將整個功能的定義放入在一個function中了 (有關這方面的知識,可參見JQuery手冊)。

  1. // create closure
  2. ( function ( $ ) ? {
  3. // plugin definition
  4. $. fn . hilight = function ( options ) ? {
  5. debug ( this ) ;
  6. ...
  7. } ;
  8. // private function for debugging
  9. function ? debug ( $ obj ) {
  10. if ? ( window . console & amp ;& amp ; window . console . log )
  11. window . console . log ( ' hilight selection count: ' + $ obj . size ()) ;
  12. } ;
  13. ...
  14. // end of closure
  15. })( jQuery ) ;

debug方法在這里是無法被在插件以外訪問到的,因此,我們稱之為它是插件私有的。

6.支持元數據插件;

根據你所寫的插件的類型,為你的插件加入元數據插件的支持將使其變得更強大。就我個人來說,喜歡采用元數據插件的重要原因便是它可以讓你使用定義之外的標簽來覆寫你的插件屬性設置(這在創建demo和例子時十分有用),而且支持它十分的簡單。
更新:這部分內容可以在本文的評論中展開討論(既然有爭議的話)

  1. // plugin definition
  2. $. fn . hilight = function ( options ) ? {
  3. ...
  4. // build main options before element iteration
  5. var ? opts = $. extend ({} , $. fn . hilight . defaults , options ) ;
  6. return ? this . each ( function () {
  7. var $ this = $ ( this ) ;
  8. // build element specific options
  9. var ? o = $. meta ? $. extend ({} , opts , $ this . data ()) : opts ;
  10. ...

改動部分的代碼會做如下的事情:
*測試metadata插件是否可用
*如果可以,將用metadata擴展options對象
這被加入到jQuery.extend,作為其最后一個參數,所以它可以覆寫任何其他參數設置。現在我們可以通過下面的方式控制其行為:

  1. <!--? markup? -->
  2. < div ? class = " hilight { background: 'red', foreground: 'white' } " > Have a nice day! </ div >
  3. < div ? class = " hilight { foreground: 'orange' } " > Have a nice day! </ div >
  4. < div ? class = " hilight { background: 'green' } " > Have a nice day! </ div >

而在調用方面,我們通過一行簡單的代碼就可以實現預期的效果:
$(’.hilight’).hilight();

將上面所述內容涉及到的代碼放在一起,完整的例子程序代碼如下:

  1. //
  2. // create closure
  3. //
  4. ( function ( $ ) ? {
  5. //
  6. // plugin definition
  7. //
  8. $. fn . hilight = function ( options ) ? {
  9. debug ( this ) ;
  10. // build main options before element iteration
  11. var ? opts = $. extend ({} , $. fn . hilight . defaults , options ) ;
  12. // iterate and reformat each matched element
  13. return ? this . each ( function () {
  14. $ this = $ ( this ) ;
  15. // build element specific options
  16. var ? o = $. meta ? $. extend ({} , opts , $ this . data ()) : opts ;
  17. // update element styles
  18. $ this . css ({
  19. backgroundColor : o . background ,
  20. color : o . foreground
  21. }) ;
  22. var ? markup = $ this . html () ;
  23. // call our format function
  24. markup = $. fn . hilight . format ( markup ) ;
  25. $ this . html ( markup ) ;
  26. }) ;
  27. } ;
  28. //
  29. // private function for debugging
  30. //
  31. function ? debug ( $ obj ) {
  32. if ? ( window . console & amp ;& amp ; window . console . log )
  33. window . console . log ( ' hilight selection count: ' + $ obj . size ()) ;
  34. } ;
  35. //
  36. // define and expose our format function
  37. //
  38. $. fn . hilight . format = function ( txt ) ? {
  39. return ? ' <strong> ' + txt + ' </strong> ' ;
  40. } ;
  41. //
  42. // plugin defaults
  43. //
  44. $. fn . hilight . defaults = {
  45. foreground : ' red ' ,
  46. background : ' yellow '
  47. } ;
  48. //
  49. // end of closure
  50. //
  51. })( jQuery ) ;