_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 已经建立了一个新的画廊插件(叫 "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 ); }
你的插件不可能在全部的状况下都能工做。一样的,当你提供不多的控制的方法的时候,它也有可能没啥用。因此,权衡是很是重要的事情。如下有三点:
灵活性:你的插件要处理多少种状况?
大小:你的插件大小和它的功能相匹配吗?
性能:你的插件不管什么状况都会处理设置选项吗?影响速度吗?值得吗?