「jQuery插件開發日記」(二)高級插件理念 - [翻譯]

_Advanced Plugin Concepts_,翻譯自 jQuery 官方網站。javascript

默認設置的共有接口

對於上一篇文章最後的代碼,咱們能夠改進,也應該改進的地方就是,爲咱們的插件的默認設置提供共有接口,使得用戶能夠直接更改默認設置。css

這樣作的好處就是可讓用戶用最少的代碼量來定製咱們的插件。此次的例子是:html

// Plugin definition.
$.fn.hilight = function( options ) {
 
    // Extend our default options with those provided.
    // Note that the first argument to extend is an empty
    // object – this is to keep from overriding our "defaults" object.
    var opts = $.extend( {}, $.fn.hilight.defaults, options );
 
    // Our plugin implementation code goes here.
 
};
 
// Plugin defaults – added as a property on our plugin function.
$.fn.hilight.defaults = {
    foreground: "red",
    background: "yellow"
};

如今用戶能夠這樣更改默認設定:java

// This needs only be called once and does not
// have to be called from within a "ready" block
$.fn.hilight.defaults.foreground = "blue";

這樣使用咱們的插件:node

$( "#myDiv" ).hilight();

你能夠看到,咱們容許用戶使用僅僅一行代碼就覆蓋了某個默認設定。除此以外,用戶仍然能夠傳入參數來覆蓋他們設定的默認設置。jquery

// Override plugin default foreground color.
$.fn.hilight.defaults.foreground = "blue";
 
// ...
 
// Invoke plugin using new defaults.
$( ".hilightDiv" ).hilight();
 
// ...
 
// Override default by passing options to plugin method.
$( "#green" ).hilight({
    foreground: "green"
});

附屬函數的公共接口

爲某些附屬函數提公共接口,可以很是好的去擴展和讓別人擴展你的插件。閉包

舉個例子,咱們的插件可能實現了一個叫 format 的函數, 這個函數格式化強調文本的形式。咱們在 hilight 函數下面定義了 format 的默認方法。app

// Plugin definition.
$.fn.hilight = function( options ) {
 
    // Iterate and reformat each matched element.
    return this.each(function() {
 
        var elem = $( this );
 
        // ...
 
        var markup = elem.html();
 
        // Call our format function.
        markup = $.fn.hilight.format( markup );
 
        elem.html( markup );
 
    });
 
};
 
// Define our format function.
$.fn.hilight.format = function( txt ) {
    return "<strong>" + txt + "</strong>";
};

咱們也能夠在設置中間容許一個回調函數,來覆蓋默認的 format 函數, 這也是一個支持定製的很是棒的方法。
使用這些技術,其餘人能夠很是方便的定製你的插件,而後發佈。換句話說,其餘人能夠爲你的插件來寫插件。ide

考慮到這篇文章裏這個蒼白的例子並非很是具備說服力,一個現實的例子就是 Cycle Plugin。這個插件是一個幻燈片插件,內置很是多切換特效,像滾動、滑動、淡出等。可是實際上它不可能定義每一個人想要的效果,那麼插件的擴展性就很是重要了。函數

Cycle Plugin 對外暴露了一個 transitions 對象。 在這裏用戶能夠本身定義他們的切換特效。

$.fn.cycle.transitions = {
 
    // ...
 
};

保持私有函數私有

對外提供一部分你的插件的公共接口確實很強大,可是你要想清楚哪些部分須要提供給公共接口,哪些不須要。當函數一旦暴露在外,任何對參數和語義(函數的功能)的更改都會摧毀向後兼容性。通常來講,若是你不肯定一個方法是否應該設爲公有,那麼也許答案是不。

因此咱們如何在不搞亂命名空間而且保持私有的狀況下定義更多的函數呢?這就是閉包的事情了。

爲了展現如何解決這個問題,咱們在咱們的插件里加了一個函數 debug。這個函數在 console 中輸出選定的對象。咱們將整個插件用一個函數包裹起來(上一篇文章也有提到)。

// Create closure.
(function( $ ) {
 
    // Plugin definition.
    $.fn.hilight = function( options ) {
        debug( this );
        // ...
    };
 
    // Private function for debugging.
    function debug( obj ) {
        if ( window.console && window.console.log ) {
            window.console.log( "hilight selection count: " + obj.length );
        }
    };
 
    // ...
 
// End of closure.
 
})( jQuery );

Bob & Sue(實例)

Bob 已經建立了一個新的畫廊插件(叫 "superGallery")。這個插件使得一系列圖片變得可導航的。Bob 一樣還添加了一些動畫效果使得插件變得更加有趣。他想讓他的插件得到最大程度的可定製性,因此他寫出了下面的代碼:

jQuery.fn.superGallery = function( options ) {
 
    // Bob's default settings:
    var defaults = {
        textColor: "#000",
        backgroundColor: "#fff",
        fontSize: "1em",
        delay: "quite long",
        getTextFromTitle: true,
        getTextFromRel: false,
        getTextFromAlt: false,
        animateWidth: true,
        animateOpacity: true,
        animateHeight: true,
        animationDuration: 500,
        clickImgToGoToNext: true,
        clickImgToGoToLast: false,
        nextButtonText: "next",
        previousButtonText: "previous",
        nextButtonTextColor: "red",
        previousButtonTextColor: "red"
    };
 
    var settings = $.extend( {}, defaults, options );
 
    return this.each(function() {
        // Plugin code would go here...
    });
 
};

你想到的第一件事情可能就是,這個插件該有多大才能實現這麼多可定製功能。這個插件的體積可能徹底不必這麼大。

Bob 對他的插件很是滿意。他以爲他的插件會在不一樣的情景之下會是一個通用的解決方案。

Sue 決定去用一用這個新插件。她設置了全部的選項,可是她意識到,若是圖片的寬度以低速變化的話,會得到更加好的效果。她趕忙去搜索了 Bob 的文檔,可是並無找到 animateWidthDuration 相似的選項。

你看到問題了嗎?

問題的關鍵是並非你的插件有多少個選項,而是有什麼選項。

Bob 作的有點兒過了。他提供的定製性看起來很高,其實很是低。特別是考慮到一我的可能想要在這個插件中控制的全部特性。Bob 犯了個錯誤,它提供了很是多荒誕的選項,卻不知讓他的插件變得更難定製了。

一個更好的模型

因此如今很是明顯了,Bob 須要一個新的定製模型。一個並無放棄必須的控制和抽象細節的定製模型。

這裏有一些建議,可讓你更好的建立一個可定製化插件。

不要建立本身的語法

使用你的插件的開發者不該該要去學一門新的語言或新的技術,只要搞定他們的工做。

Bob 他爲 delay 這個選項提供了最大的定製性。他設置了4個不一樣的延遲:

var delayDuration = 0;
 
switch ( settings.delay ) {
 
    case "very short":
        delayDuration = 100;
        break;
 
    case "quite short":
        delayDuration = 200;
        break;
 
    case "quite long":
        delayDuration = 300;
        break;
 
    case "very long":
        delayDuration = 400;
        break;
 
    default:
        delayDuration = 200;
 
}

這致使不只僅用戶所能控制的延遲水平變少了,還花費了比較多的空間。20行代碼僅僅就爲了定義一個延遲時間,有點兒多了。

一個更加好的方式就是讓用戶本身傳入延遲的時間。

元素的徹底控制權

若是你的插件在DOM中建立了一些元素。那麼你最好讓用戶有方法去控制它。有些時候這意味着提供給用戶傳入ID或者類名的方法。可是注意你的插件不該該全局依賴這些。

一個很差的實現:

// Plugin code
$( "<div class='gallery-wrapper' />" ).appendTo( "body" );

一個好一點兒的實現:

// Retain an internal reference:
var wrapper = $( "<div />" )
    .attr( settings.wrapperAttrs )
    .appendTo( settings.container );
 
// Easy to reference later...
wrapper.append( "..." );

注意咱們使用了 .attr() 來增長特定的屬性。因此咱們的設置應該像這樣:

var defaults = {
    wrapperAttrs : {
        class: "gallery-wrapper"
    },
    // ... rest of settings ...
};
 
// We can use the extend method to merge options/settings as usual:
// But with the added first parameter of TRUE to signify a DEEP COPY:
var settings = $.extend( true, {}, defaults, options );

對於CSS 也可使用一樣的方法來實現:

var defaults = {
    wrapperCSS: {},
    // ... rest of settings ...
};
 
// Later on in the plugin where we define the wrapper:
var wrapper = $( "<div />" )
    .attr( settings.wrapperAttrs )
    .css( settings.wrapperCSS ) // ** Set CSS!
    .appendTo( settings.container );

提供回調函數

若是你的插件是事件驅動的話,最好爲每一個事件提供回調函數。

var defaults = {
 
    // We define an empty anonymous function so that
    // we don't need to check its existence before calling it.
    onImageShow : function() {},
 
    // ... rest of settings ...
 
};
 
// Later on in the plugin:
 
nextButton.on( "click", showNextImage );
 
function showNextImage() {
 
    // Returns reference to the next image node
    var image = getNextImage();
 
    // Stuff to show the image here...
 
    // Here's the callback:
    settings.onImageShow.call( image );
}

記住,這是一個權衡問題

你的插件不可能在全部的狀況下都能工做。一樣的,當你提供不多的控制的方法的時候,它也有可能沒啥用。因此,權衡是很是重要的事情。如下有三點:

  • 靈活性:你的插件要處理多少種狀況?

  • 大小:你的插件大小和它的功能相匹配嗎?

  • 性能:你的插件不管什麼狀況都會處理設置選項嗎?影響速度嗎?值得嗎?

相關文章
相關標籤/搜索