<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # jQuery 插件的設計模式 jQuery插件開發在過去幾年里進步了很多. 我們寫插件的方式不再僅僅只有一種,相反有很多種。現實中,某些插件設計模式在解決某些特殊的問題或者開發組件的時候比其他模式更有效。 有些開發者可能希望使用 jQuery UI 部件工廠; 它對于創建復雜而又靈活的UI組件是很強大的。有些開發者可能不想使用。 有些開發者可能想把它們的插件設計得更像模塊(與模塊模式相似)或者使用一種更現代化的模塊格式。 有些開發者想讓他們的插件利用原型繼承的特點。有些則希望使用自定義事件或者通過發布/訂閱信息來實現插件和他們的其它App之間的通信。 在經過了想創建一刀切的jquery插件樣板的數次嘗試之后,我開始考慮插件模式。這樣的樣板在理論上是一個很好的主意,但現實是,我們很少使用固定的方式并且總是使用一種模式來寫插件。 讓我們假設我們已經為了某個目標去著手嘗試編寫我們自己的jQuery插件,并且我們可以放心的把一些東西放在一起運作。它是起作用的。它做了它需要去做的,但是也許我們覺得它可以被構造得更好。也許它應該更加靈活或者被設計用來解決更多開發者普遍都碰到過的問題才對。如果這聽起來很熟悉,那么你也許會發現這一章是很有用的。在其中,我們將探討大量的jQuery插件模式,它們在其它開發者的環境中都工作的不錯。 > 注意:盡管開頭我們將簡要回顧一些jQuery插件的基礎知識,但這一章是針對中級到高級的開發者的。 如果你覺得對此還沒有做足夠的準備,我很高興的建議你去看一看jQuery官方的插件/創作(Plugins/Authoring )指導,Ben Alman的插件類型指導(plugin style guide)和RemySharp的“寫得不好的jQuery插件的癥候(Signs of a Poorly Written jQuery Plugin)”,作為開始這一節之前的閱讀材料。 ## 模式 jQuery插件有一些具體的規則,它們是整個社區能夠實現這令人難以置信的多樣性的原因之一。在最基本的層面上,我們能夠編寫一個簡單地向jQuery的jQuery.fn對象添加一個新的功能屬性的插件,像下面這樣: ~~~ $.fn.myPluginName = function () { // our plugin logic }; ~~~ 對于緊湊性而言這是很棒的,而下面的代碼將會是一個更好的構建基礎: ~~~ (function( $ ){ $.fn.myPluginName = function () { // our plugin logic }; })( jQuery ); ~~~ 在這里,我們將我們的插件邏輯封裝到一個匿名函數中。為了確保我們使用的$標記作為簡寫形式不會造成任何jQuery和其它Javascript庫之間的沖突,我們簡單的將其傳入這個閉包中,它會將其映射到美元符號上。這就確保了它能夠不被任何范圍之外的執行影響到。 編寫這種模式的一個可選方式是使用jQuery.extend(),它使得我們能夠一次定義多個函數,并且有時能夠獲得更多的語義上的意義。 ~~~ (function( $ ){ $.extend($.fn, { myplugin: function(){ // your plugin logic } }); })( jQuery ); ~~~ 現在我們已經回顧了一些jQuery插件的基礎,但是許多更多的工作可借以更進一步。A Lightweight Start是我們將要探討的該 設計模式的第一個完整的插件,它涵蓋了我們可以在每天的基礎的插件開發工作中用到的一些最佳實踐, 細數了一些值得推廣應用的常見問題描述。 > 注意: 盡管下面大多數的模式都會得到解釋,我還是建議大家通過閱讀代碼里的注釋來研究它們,因為這些注釋能夠提供關于為什么一個具體的最佳實踐會被應用這個問題的更深入的理解。 我也應該提醒下,沒有前面的工作往后這些沒有一樣是可能的,它們是來自于jQuery社區的其他成員的輸入和建議。我已經將它們列到每一種模式中了,以便諸位可以根據各自的工作方向來閱讀相關的內容,如果感興趣的話。 ## A Lightweight Start 模式 讓我們用一些遵循了(包括那些在jQuery 插件創作指導中的)最佳實踐的基礎的東西來開始我們針對插件模式的深入探討。這一模式對于插件開發的新手和只想要實現一些簡單的東西(例如工具插件)的人來說是理想的。A Lightweight Start 使用到了下面這些東西: * 諸如分號放置在函數調用之前這樣一些通用的最佳實踐(我們將在下面的注釋中解釋為什么要這樣做) * window,document,undefined作為參數傳入。 * 基本的默認對象。 * 一個簡單的針對跟初始化創建和要一起運作的元素的賦值相關的邏輯的插件構造器。 * 擴展默認的選項。 * 圍繞構造器的輕量級的封裝,它有助于避免諸如實例化多次的問題。 * 堅持最大限度可讀性的jQuery核心風格的指導方針。 ~~~ /*! * jQuery lightweight plugin boilerplate * Original author: @ajpiano * Further changes, comments: @addyosmani * Licensed under the MIT license */ // the semi-colon before the function invocation is a safety // net against concatenated scripts and/or other plugins // that are not closed properly. ;(function ( $, window, document, undefined ) { // undefined is used here as the undefined global // variable in ECMAScript 3 and is mutable (i.e. it can // be changed by someone else). undefined isn't really // being passed in so we can ensure that its value is // truly undefined. In ES5, undefined can no longer be // modified. // window and document are passed through as local // variables rather than as globals, because this (slightly) // quickens the resolution process and can be more // efficiently minified (especially when both are // regularly referenced in our plugin). // Create the defaults once var pluginName = "defaultPluginName", defaults = { propertyName: "value" }; // The actual plugin constructor function Plugin( element, options ) { this.element = element; // jQuery has an extend method that merges the // contents of two or more objects, storing the // result in the first object. The first object // is generally empty because we don't want to alter // the default options for future instances of the plugin this.options = $.extend( {}, defaults, options) ; this._defaults = defaults; this._name = pluginName; this.init(); } Plugin.prototype.init = function () { // Place initialization logic here // We already have access to the DOM element and // the options via the instance, e.g. this.element // and this.options }; // A really lightweight plugin wrapper around the constructor, // preventing against multiple instantiations $.fn[pluginName] = function ( options ) { return this.each(function () { if ( !$.data(this, "plugin_" + pluginName )) { $.data( this, "plugin_" + pluginName, new Plugin( this, options )); } }); } })( jQuery, window, document ); ~~~ 用例: ~~~ $("#elem").defaultPluginName({ propertyName: "a custom value" }); ~~~ ## 完整的 Widget 工廠模式 雖然jQuery插件創作指南是對插件開發的一個很棒的介紹,但它并不能幫助掩蓋我們不得不定期處理的常見的插件管道任務。 jQuery UI Widget工廠是這個問題的一種解決方案,能幫助我們基于面向對象原則構建復雜的,具有狀態性的插件。它也簡化了我們插件實體的通信,也淡化了許多我們在一些基礎的插件上工作時必須去編寫代碼的重復性的工作。 具有狀態性的插件幫助我們對它們的當前狀態保持跟進,也允許我們在插件被初始化之后改變其屬性。 有關Widget工廠最棒的事情之一是大部分的jQuery UI庫的實際上都是使用它作為其組件的基礎。這意味著如果我們是在尋找超越這一模式的架構的進一步指導,我們將沒必要去超越GitHub上的[jQuery UI](https://github.com/jquery/jquery-ui)進行思考。 jQuery UI Widget 工廠模式涵蓋了包括事件觸發在內幾乎所有的默認支持的工廠方法。每一個模式的最后都包含了所有這些方法的使用注釋,還在內嵌的注釋中給出了更深入的指導。 ~~~ /*! * jQuery UI Widget-factory plugin boilerplate (for 1.8/9+) * Author: @addyosmani * Further changes: @peolanha * Licensed under the MIT license */ ;(function ( $, window, document, undefined ) { // define our widget under a namespace of your choice // with additional parameters e.g. // $.widget( "namespace.widgetname", (optional) - an // existing widget prototype to inherit from, an object // literal to become the widget's prototype ); $.widget( "namespace.widgetname" , { //Options to be used as defaults options: { someValue: null }, //Setup widget (e.g. element creation, apply theming // , bind events etc.) _create: function () { // _create will automatically run the first time // this widget is called. Put the initial widget // setup code here, then we can access the element // on which the widget was called via this.element. // The options defined above can be accessed // via this.options this.element.addStuff(); }, // Destroy an instantiated plugin and clean up // modifications the widget has made to the DOM destroy: function () { // this.element.removeStuff(); // For UI 1.8, destroy must be invoked from the // base widget $.Widget.prototype.destroy.call( this ); // For UI 1.9, define _destroy instead and don't // worry about // calling the base widget }, methodB: function ( event ) { //_trigger dispatches callbacks the plugin user // can subscribe to // signature: _trigger( "callbackName" , [eventObject], // [uiObject] ) // e.g. this._trigger( "hover", e /*where e.type == // "mouseenter"*/, { hovered: $(e.target)}); this._trigger( "methodA", event, { key: value }); }, methodA: function ( event ) { this._trigger( "dataChanged", event, { key: value }); }, // Respond to any changes the user makes to the // option method _setOption: function ( key, value ) { switch ( key ) { case "someValue": // this.options.someValue = doSomethingWith( value ); break; default: // this.options[ key ] = value; break; } // For UI 1.8, _setOption must be manually invoked // from the base widget $.Widget.prototype._setOption.apply( this, arguments ); // For UI 1.9 the _super method can be used instead // this._super( "_setOption", key, value ); } }); })( jQuery, window, document ); ~~~ 用例: ~~~ var collection = $("#elem").widgetName({ foo: false }); collection.widgetName("methodB"); ~~~ ## 嵌套的命名空間插件模式 如我們在本書的前面所述,為我們的代碼加入命名空間是避免與其它的全局命名空間中的對象和變量產生沖突的一種方法。它們是很重要的,因為我們想要保護我們的插件的運作不會突然被頁面上另外一段使用了同名變量或者插件的腳本所打斷。作為全局命名空間的好市民,我們也必須盡我們所能來阻止其他開發者的腳本由于同樣的問題而執行起來發生問題。 Javascript并不像其它語言那樣真的內置有對命名空間的支持,但它卻有可以被用來達到同樣效果的對象。雇傭一個頂級對象作為我們命名空間的名稱,我們就可以使用相同的名字檢查頁面上另外一個對象的存在性。如果這樣的對象不存在,那么我們就定義它;如果它存在,就簡單的用我們的插件對其進行擴展。 對象(或者更確切的說,對象常量)可以被用來創建內嵌的命名空間,namespace.subnamespace.pluginName,諸如此類。而為了保持簡單,下面的命名空間樣板會向我們展示有關這些概念的入門我們所需要的一切。 ~~~ /*! * jQuery namespaced "Starter" plugin boilerplate * Author: @dougneiner * Further changes: @addyosmani * Licensed under the MIT license */ ;(function ( $ ) { if (!$.myNamespace) { $.myNamespace = {}; }; $.myNamespace.myPluginName = function ( el, myFunctionParam, options ) { // To avoid scope issues, use "base" instead of "this" // to reference this class from internal events and functions. var base = this; // Access to jQuery and DOM versions of element base.$el = $( el ); base.el = el; // Add a reverse reference to the DOM object base.$el.data( "myNamespace.myPluginName" , base ); base.init = function () { base.myFunctionParam = myFunctionParam; base.options = $.extend({}, $.myNamespace.myPluginName.defaultOptions, options); // Put our initialization code here }; // Sample Function, Uncomment to use // base.functionName = function( parameters ){ // // }; // Run initializer base.init(); }; $.myNamespace.myPluginName.defaultOptions = { myDefaultValue: "" }; $.fn.mynamespace_myPluginName = function ( myFunctionParam, options ) { return this.each(function () { (new $.myNamespace.myPluginName( this, myFunctionParam, options )); }); }; })( jQuery ); ~~~ 用例: ~~~ $("#elem").mynamespace_myPluginName({ myDefaultValue: "foobar" }); ~~~ ## (使用Widget工廠)自定義事件插件模式 在本書的Javascript設計模式一節,我們討論了觀察者模式,而后繼續論述到了jQuery對于自定義事件的支持,其為實現發布/訂閱提供了一種類似的解決方案。 這里的基本觀點是當我們的應用程序中發生了某些有趣的事情時,頁面中的對象能夠發布事件通知。其他對象就會訂閱(或者偵聽)這些事件,并且據此產生回應。我們應用程序的這一邏輯所產生的效果是更加顯著的解耦,每一個對象不再需要直接同另外一個對象進行通信。 在接下來的jQuery UI widget工廠模式中,我們將實現一個基本的基于自定義事件的發布/訂閱系統,它允許我們的插件向應用程序的其余部分發布事件通知,而這些部分將對此產生回應。 ~~~ /*! * jQuery custom-events plugin boilerplate * Author: DevPatch * Further changes: @addyosmani * Licensed under the MIT license */ // In this pattern, we use jQuery's custom events to add // pub/sub (publish/subscribe) capabilities to widgets. // Each widget would publish certain events and subscribe // to others. This approach effectively helps to decouple // the widgets and enables them to function independently. ;(function ( $, window, document, undefined ) { $.widget( "ao.eventStatus", { options: { }, _create : function() { var self = this; //self.element.addClass( "my-widget" ); //subscribe to "myEventStart" self.element.on( "myEventStart", function( e ) { console.log( "event start" ); }); //subscribe to "myEventEnd" self.element.on( "myEventEnd", function( e ) { console.log( "event end" ); }); //unsubscribe to "myEventStart" //self.element.off( "myEventStart", function(e){ ///console.log( "unsubscribed to this event" ); //}); }, destroy: function(){ $.Widget.prototype.destroy.apply( this, arguments ); }, }); })( jQuery, window , document ); // Publishing event notifications // $( ".my-widget" ).trigger( "myEventStart"); // $( ".my-widget" ).trigger( "myEventEnd" ); ~~~ 用例: ~~~ var el = $( "#elem" ); el.eventStatus(); el.eventStatus().trigger( "myEventStart" ); ~~~ ## 使用DOM-To-Object橋接模式的原型繼承 正如前面所介紹的,在Javascript中,我們并不需要那些在其它經典的編程語言中找到的類的傳統觀念,但我們確實需要原型繼承。有了原型繼承,對象就可以從其它對象繼承而來了。我們可以將此概念應用到jQuery的插件開發中。 Yepnope.js作者Alex Sexton和jQuery團隊成員Scott Gonzalez已經矚目于這個主題的細節。總之,他們發現為了組織模塊化的開發,使定義插件邏輯的對象同插件生成過程本身分離是有好處的。 這一好處就是對我們插件代碼的測試會變得顯著的簡單起來,并且我們也能夠在不改變任何我們所實現的對象API的方式,這一前提下,適應事物在幕后運作的方式。 在Sexton關于這個主題的文章中,他實現了一個使我們能夠將我們的一般的邏輯附加到特定插件的橋,我們已經在下面的模式中將它實現。 這一模式的另外一個優點是我們不需要去不斷的重復同樣的插件初始化代碼,這確保了DRY開發背后的觀念得以維持。一些開發者或許也會發現這一模式的代碼相比其它更加易讀。 ~~~ /*! * jQuery prototypal inheritance plugin boilerplate * Author: Alex Sexton, Scott Gonzalez * Further changes: @addyosmani * Licensed under the MIT license */ // myObject - an object representing a concept we wish to model // (e.g. a car) var myObject = { init: function( options, elem ) { // Mix in the passed-in options with the default options this.options = $.extend( {}, this.options, options ); // Save the element reference, both as a jQuery // reference and a normal reference this.elem = elem; this.$elem = $( elem ); // Build the DOM's initial structure this._build(); // return this so that we can chain and use the bridge with less code. return this; }, options: { name: "No name" }, _build: function(){ //this.$elem.html( "<h1>"+this.options.name+"</h1>" ); }, myMethod: function( msg ){ // We have direct access to the associated and cached // jQuery element // this.$elem.append( "<p>"+msg+"</p>" ); } }; // Object.create support test, and fallback for browsers without it if ( typeof Object.create !== "function" ) { Object.create = function (o) { function F() {} F.prototype = o; return new F(); }; } // Create a plugin based on a defined object $.plugin = function( name, object ) { $.fn[name] = function( options ) { return this.each(function() { if ( ! $.data( this, name ) ) { $.data( this, name, Object.create( object ).init( options, this ) ); } }); }; }; ~~~ 用例: ~~~ $.plugin( "myobj", myObject ); $("#elem").myobj( {name: "John"} ); var collection = $( "#elem" ).data( "myobj" ); collection.myMethod( "I am a method"); ~~~ ## jQuery UI Widget 工廠橋接模式 如果你喜歡基于過去的設計模式的對象的生成插件這個主意,那么你也許會對這個在jQuery UI Widget工廠中發現的叫做$.widget.bridge的方法感興趣。 這座橋基本上是在充當使用$.widget創建的Javascript對象和jQuery核心API之間的中間層,它提供了一種實現基于對象的插件定義的更加內置的解決方案。實際上,我們能夠使用自定義的構造器去創建具有狀態性的插件。 此外,$.widget.bridge還提供了對許多其它功能的訪問,包括下面這些: * 公共的和私有的方法都如人們在經典的OOP中所希望的方式被處理(例如,公共的方法被暴露出來,而對私有方法的調用則是不可能的)。 * 防止多次初始化的自動保護。 * 傳入對象實體的自動生成,而對它們的存儲則在內置的$.datacache范圍之內。 * 選項可以在初始化后修改。 有關使用這一模式的更多信息,請看看下面內嵌的注釋: ~~~ /*! * jQuery UI Widget factory "bridge" plugin boilerplate * Author: @erichynds * Further changes, additional comments: @addyosmani * Licensed under the MIT license */ // a "widgetName" object constructor // required: this must accept two arguments, // options: an object of configuration options // element: the DOM element the instance was created on var widgetName = function( options, element ){ this.name = "myWidgetName"; this.options = options; this.element = element; this._init(); } // the "widgetName" prototype widgetName.prototype = { // _create will automatically run the first time this // widget is called _create: function(){ // creation code }, // required: initialization logic for the plugin goes into _init // This fires when our instance is first created and when // attempting to initialize the widget again (by the bridge) // after it has already been initialized. _init: function(){ // init code }, // required: objects to be used with the bridge must contain an // "option". Post-initialization, the logic for changing options // goes here. option: function( key, value ){ // optional: get/change options post initialization // ignore if you don't require them. // signature: $("#foo").bar({ cool:false }); if( $.isPlainObject( key ) ){ this.options = $.extend( true, this.options, key ); // signature: $( "#foo" ).option( "cool" ); - getter } else if ( key && typeof value === "undefined" ){ return this.options[ key ]; // signature: $( "#foo" ).bar("option", "baz", false ); } else { this.options[ key ] = value; } // required: option must return the current instance. // When re-initializing an instance on elements, option // is called first and is then chained to the _init method. return this; }, // notice no underscore is used for public methods publicFunction: function(){ console.log( "public function" ); }, // underscores are used for private methods _privateFunction: function(){ console.log( "private function" ); } }; ~~~ 用例: ~~~ // connect the widget obj to jQuery's API under the "foo" namespace $.widget.bridge( "foo", widgetName ); // create an instance of the widget for use var instance = $( "#foo" ).foo({ baz: true }); // our widget instance exists in the elem's data // Outputs: #elem console.log(instance.data( "foo" ).element); // bridge allows us to call public methods // Outputs: "public method" instance.foo("publicFunction"); // bridge prevents calls to internal methods instance.foo("_privateFunction"); ~~~ ## 使用 Widget 工廠的 jQuery Mobile 小部件 jQuery Mobile 是一個 jQuery 項目框架,為設計同時能運行在主流移動設備和平臺以及桌面平臺的大多數常見 Web 應用帶來便利。我們可以僅編寫一次代碼,而無需為每種設備或操作系統編寫特定的應用,就能使其同時運行在 A、B 和 C 級瀏覽器。 JQuery mobile 背后的基本原理也可應用于插件和小部件的開發。 接下來介紹的模式令人感興趣的是,已熟悉使用 jQuery UI Widget Factory 模式的開發者能夠很快地編寫針對移動設備優化的小部件,即便這會在不同設備中存在細微的差異。 下面為移動優化的widget同前面我們看到的標準UI widget模式相比,有許多有趣的不同之處。 $.mobile.widget 是繼承于現有的widget原型的引用。對于標準的widget, 通過任何這樣的原型進行基礎的開發都是沒有必要的,但是使用這種為移動應用定制的jQuery widget 原型,它提供了更多的“選項”格式供內部訪問。 在_create()中,教程提供了關于官方的jQuery 移動 widget如何處理元素選擇,對于基于角色的能夠更好的適應jQM標記的方法的選擇。這并不是說標準的選擇不被推薦,只是說這種方法也許可以給予jQuery 移動頁面的架構更多的意義。 也有以注釋形式提供的關于將我們的插件方法應用于頁面創建,還有通過數據角色和數據屬性選擇插件應用程序,這些內容的指導。 ~~~ /*! * (jQuery mobile) jQuery UI Widget-factory plugin boilerplate (for 1.8/9+) * Author: @scottjehl * Further changes: @addyosmani * Licensed under the MIT license */ ;(function ( $, window, document, undefined ) { // define a widget under a namespace of our choice // here "mobile" has been used in the first argument $.widget( "mobile.widgetName", $.mobile.widget, { // Options to be used as defaults options: { foo: true, bar: false }, _create: function() { // _create will automatically run the first time this // widget is called. Put the initial widget set-up code // here, then we can access the element on which // the widget was called via this.element // The options defined above can be accessed via // this.options // var m = this.element, // p = m.parents( ":jqmData(role="page")" ), // c = p.find( ":jqmData(role="content")" ) }, // Private methods/props start with underscores _dosomething: function(){ ... }, // Public methods like these below can can be called // externally: // $("#myelem").foo( "enable", arguments ); enable: function() { ... }, // Destroy an instantiated plugin and clean up modifications // the widget has made to the DOM destroy: function () { // this.element.removeStuff(); // For UI 1.8, destroy must be invoked from the // base widget $.Widget.prototype.destroy.call( this ); // For UI 1.9, define _destroy instead and don't // worry about calling the base widget }, methodB: function ( event ) { //_trigger dispatches callbacks the plugin user can // subscribe to // signature: _trigger( "callbackName" , [eventObject], // [uiObject] ) // e.g. this._trigger( "hover", e /*where e.type == // "mouseenter"*/, { hovered: $(e.target)}); this._trigger( "methodA", event, { key: value }); }, methodA: function ( event ) { this._trigger( "dataChanged", event, { key: value }); }, // Respond to any changes the user makes to the option method _setOption: function ( key, value ) { switch ( key ) { case "someValue": // this.options.someValue = doSomethingWith( value ); break; default: // this.options[ key ] = value; break; } // For UI 1.8, _setOption must be manually invoked from // the base widget $.Widget.prototype._setOption.apply(this, arguments); // For UI 1.9 the _super method can be used instead // this._super( "_setOption", key, value ); } }); })( jQuery, window, document ); ~~~ 用例: ~~~ var instance = $( "#foo" ).widgetName({ foo: false }); instance.widgetName( "methodB" ); ~~~ 不論什么時候jQuery Mobile中的一個新頁面被創建了,我們也都可以自己初始化這個widget。但一個(通過data-role="page"屬性發現的)jQuery Mobile 頁面一開始被初始化時, jQuery Mobile的頁面插件會自己派發一個創建事件。我們能偵聽那個(稱作 “pagecreate”的)事件,并且在任何時候只要新的頁面一被創建,就自動的讓我們的插件運行。 ~~~ $(document).on("pagecreate", function ( e ) { // In here, e.target refers to the page that was created // (it's the target of the pagecreate event) // So, we can simply find elements on this page that match a // selector of our choosing, and call our plugin on them. // Here's how we'd call our "foo" plugin on any element with a // data-role attribute of "foo": $(e.target).find( "[data-role="foo"]" ).foo( options ); // Or, better yet, let's write the selector accounting for the configurable // data-attribute namespace $( e.target ).find( ":jqmData(role="foo")" ).foo( options ); }); ~~~ 現在我們可以在一個頁面中簡單的引用包含了我們的widget和pagecreate綁定的腳本,而它將像任何其它的jQuery Mobile插件一樣自動的運行。 ## RequireJS 和 jQuery UI Widget 工廠 如我們在當代模塊化設計模式一節所述,RequireJS是一種兼容AMD的腳本裝載器,它提供了將應用程序邏輯封裝到可管理的模塊中,這樣一個干凈的解決方案。 它能夠(通過它的順序插件)將模塊按照正確的順序加載,簡化了借助它優秀的r.js優化器整合腳本的過程,并且提供了在每一個模塊的基礎上定義動態依賴的方法。 在下面的樣板模式中,我們展示了一種兼容AMD的jQuery UI widget(這里是RequireJS)如何能夠被定義成做到下面這些事情: * 允許widget模塊依賴的定義,構建在前面早先的jQuery UI Widget 工廠模式之上。 * 展示一種為創建(使用Underscore.js 微模板)模板化的widget傳入HTML模板集的方法。 * 包括一種如果我們希望晚一點將其傳入到RequireJS優化器,以使我們能夠對我們的widget模塊做出調整的快速提示。 ~~~ /*! * jQuery UI Widget + RequireJS module boilerplate (for 1.8/9+) * Authors: @jrburke, @addyosmani * Licensed under the MIT license */ // Note from James: // // This assumes we are using the RequireJS+jQuery file, and // that the following files are all in the same directory: // // - require-jquery.js // - jquery-ui.custom.min.js (custom jQuery UI build with widget factory) // - templates/ // - asset.html // - ao.myWidget.js // Then we can construct the widget as follows: // ao.myWidget.js file: define( "ao.myWidget", ["jquery", "text!templates/asset.html", "underscore", "jquery-ui.custom.min"], function ( $, assetHtml, _ ) { // define our widget under a namespace of our choice // "ao" is used here as a demonstration $.widget( "ao.myWidget", { // Options to be used as defaults options: {}, // Set up widget (e.g. create element, apply theming, // bind events, etc.) _create: function () { // _create will automatically run the first time // this widget is called. Put the initial widget // set-up code here, then we can access the element // on which the widget was called via this.element. // The options defined above can be accessed via // this.options // this.element.addStuff(); // this.element.addStuff(); // We can then use Underscore templating with // with the assetHtml that has been pulled in // var template = _.template( assetHtml ); // this.content.append( template({}) ); }, // Destroy an instantiated plugin and clean up modifications // that the widget has made to the DOM destroy: function () { // this.element.removeStuff(); // For UI 1.8, destroy must be invoked from the base // widget $.Widget.prototype.destroy.call( this ); // For UI 1.9, define _destroy instead and don't worry // about calling the base widget }, methodB: function ( event ) { // _trigger dispatches callbacks the plugin user can // subscribe to // signature: _trigger( "callbackName" , [eventObject], // [uiObject] ) this._trigger( "methodA", event, { key: value }); }, methodA: function ( event ) { this._trigger("dataChanged", event, { key: value }); }, // Respond to any changes the user makes to the option method _setOption: function ( key, value ) { switch (key) { case "someValue": // this.options.someValue = doSomethingWith( value ); break; default: // this.options[ key ] = value; break; } // For UI 1.8, _setOption must be manually invoked from // the base widget $.Widget.prototype._setOption.apply( this, arguments ); // For UI 1.9 the _super method can be used instead // this._super( "_setOption", key, value ); } }); }); ~~~ 用例: index.html: ~~~ <script data-main="scripts/main" src="http://requirejs.org/docs/release/1.0.1/minified/require.js"></script> ~~~ main.js ~~~ require({ paths: { "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min", "jqueryui": "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min", "boilerplate": "../patterns/jquery.widget-factory.requirejs.boilerplate" } }, ["require", "jquery", "jqueryui", "boilerplate"], function (req, $) { $(function () { var instance = $("#elem").myWidget(); instance.myWidget("methodB"); }); }); ~~~ ## 全局和每次調用的重載選項(最佳調用模式) 對于我們的下一個模式,我們將來看看一種為插件選擇默認和手動配置選項的優化了的方法。 定義插件選項,我們大多數人可能熟悉的一種方法是,通過默認的字面上的對象將其傳遞到$.extend(),如我們在我們基礎的插件樣板中所展示的。 然而,如果我們正工作在一種帶有許多的定制選項,對于這些定制選項我們希望用戶在全局和每一次調用的級別都能重載,那樣我們就能以更加優化一點的方式構造事物。 相反,通過明確的引用定義在插件命名空間中的一個選項對象(例如,$fn.pluginName.options),還有將此同任何在其最初被調用時傳遞到插件的選項混合,用戶就要對在插件初始化期間傳遞選項,或者在插件外部重載選項,這兩者有所選擇(如這里所展示的)。 ~~~ /*! * jQuery "best options" plugin boilerplate * Author: @cowboy * Further changes: @addyosmani * Licensed under the MIT license */ ;(function ( $, window, document, undefined ) { $.fn.pluginName = function ( options ) { // Here's a best practice for overriding "defaults" // with specified options. Note how, rather than a // regular defaults object being passed as the second // parameter, we instead refer to $.fn.pluginName.options // explicitly, merging it with the options passed directly // to the plugin. This allows us to override options both // globally and on a per-call level. options = $.extend( {}, $.fn.pluginName.options, options ); return this.each(function () { var elem = $(this); }); }; // Globally overriding options // Here are our publicly accessible default plugin options // that are available in case the user doesn't pass in all // of the values expected. The user is given a default // experience but can also override the values as necessary. // e.g. $fn.pluginName.key ="otherval"; $.fn.pluginName.options = { key: "value", myMethod: function ( elem, param ) { } }; })( jQuery, window, document ); ~~~ 用例: ~~~ $("#elem").pluginName({ key: "foobar" }); ~~~ ## 高可配置和可變插件模式 在這個模式中,同Alex Sexton的原型繼承插件模式類似,我們插件的邏輯并不嵌套在一個jQuery插件自身之中.取而代之我們使用了一個構造器和一種定義在它的原型之上的對象字面值,來定義我們的插件邏輯.jQuery隨后被用在插件對象的實際實例中。 通過玩了兩個小花樣,定制被帶到了一個新的層次,其中之一就是我們在前面已經看到的模式: * 選項不論是全局的還是集合中每一個元素的,都可以被重載。 * 選在可以通過HTML5數據屬性(在下面會有展示)在每一個元素的級別被定制.這有利于可以被應用到集合中元素的插件行為,但是會導致在不需要使用一個不同的默認值實例化每一個元素的前提下定制的內聯。 在不怎么正規的場合我們不會經常見到這種非常規的選項,但是它能夠成為一種重要的清晰方案(只要我們不介意這種內聯的方式).如果不知道這個東西在那兒會起作用,那就想象著要為大型的元素集合編寫一個可拖動的插件,這種場景.我們可以像下面這樣定制它們的選項: ~~~ $( ".item-a" ).draggable( {"defaultPosition":"top-left"} ); $( ".item-b" ).draggable( {"defaultPosition":"bottom-right"} ); $( ".item-c" ).draggable( {"defaultPosition":"bottom-left"} ); //etc ~~~ 但是使用我們模式的內聯方式,下面這樣是可能的。 ~~~ $( ".items" ).draggable(); ~~~ ~~~ html <li class="item" data-plugin-options="{"defaultPosition":"top-left"}"></div> <li class="item" data-plugin-options="{"defaultPosition":"bottom-left"}"></div> ~~~ 諸如此類.我們也許更加偏好這些方法之一,但它僅僅是我們值得去意識到的另外一個差異。 ~~~ /* * "Highly configurable" mutable plugin boilerplate * Author: @markdalgleish * Further changes, comments: @addyosmani * Licensed under the MIT license */ // Note that with this pattern, as per Alex Sexton's, the plugin logic // hasn't been nested in a jQuery plugin. Instead, we just use // jQuery for its instantiation. ;(function( $, window, document, undefined ){ // our plugin constructor var Plugin = function( elem, options ){ this.elem = elem; this.$elem = $(elem); this.options = options; // This next line takes advantage of HTML5 data attributes // to support customization of the plugin on a per-element // basis. For example, // <div class=item" data-plugin-options="{"message":"Goodbye World!"}"></div> this.metadata = this.$elem.data( "plugin-options" ); }; // the plugin prototype Plugin.prototype = { defaults: { message: "Hello world!" }, init: function() { // Introduce defaults that can be extended either // globally or using an object literal. this.config = $.extend( {}, this.defaults, this.options, this.metadata ); // Sample usage: // Set the message per instance: // $( "#elem" ).plugin( { message: "Goodbye World!"} ); // or // var p = new Plugin( document.getElementById( "elem" ), // { message: "Goodbye World!"}).init() // or, set the global default message: // Plugin.defaults.message = "Goodbye World!" this.sampleMethod(); return this; }, sampleMethod: function() { // e.g. show the currently configured message // console.log(this.config.message); } } Plugin.defaults = Plugin.prototype.defaults; $.fn.plugin = function( options ) { return this.each(function() { new Plugin( this, options ).init(); }); }; // optional: window.Plugin = Plugin; })( jQuery, window , document ); ~~~ 用例: ~~~ $("#elem").plugin({ message: "foobar" }); ~~~ ## 是什么造就了模式之外的一個優秀插件? 在今天結束之際,設計模式僅僅只是編寫可維護的jQuery插件的一個方面。還有大量其它的因素值得考慮,而我也希望分享下對于用第三方插件來解決一些其它的問題,我自己的選擇標準。 質量 對于你所寫的Javascript和jQuery插件,請堅持遵循最佳實踐的做法。是否使用jsHint或者jsLint努力使插件更加厚實了呢?插件是否被優化過了呢? 編碼風格 插件是否遵循了諸如jQuery 核心風格指南這樣一種一致的風格指南?如果不是,那么你的代碼至少是不是相對干凈,并且可讀的? 兼容性 各個版本的jQuery插件兼容怎么樣?通過對編譯jQuery-git源碼的版本或者最新的穩定版的測試,如果在jQuery 1.6發布之前寫的插件,那么它可能存在有問題的屬性和特性,因為他們隨著新版的發布而改變。 新版本的jQuery為jQuery的項目的提高核心庫的使用提供了的改進和環境,雖然偶然出現破損(主要的版本),但我們是朝著更好的方向做的,我看到插件作者在必要時更新自己的代碼,至少,測試他們的新版本的插件,以確保一切都如預期般運行。 可靠性 這個插件應該有自己的一套單元測試。做這些不僅是為了證明它確實在按照預期運作,也可以改進設計而無需影響最終用戶。我認為單元測試對任何重要的jQuery插件都是必要的,它們對生產環境意義重大,而且它們也不是那么難寫。要想獲得一個用QUnit實現自動化JavaScript測試的完美指南,你也許會對J?rn Zaefferer的“使用QUnit自動化JavaScript 測試”感興趣。 性能 如果這個插件需要執行的任務包含有大量的處理語句,或者對DOM的大量處理,那就應該按照基準管理的最佳實踐將這個任務最小化。用jsPerf.com 來測試代碼段,以實現 a) 在不同的瀏覽器執行是否良好 以及 b)如果存在的話,找到可以進一步優化的地方。 文檔 如果目的是讓其他開發者使用插件,那就要保證它具有良好的文檔。給API建立文檔描述這些插件是如何使用的。這個插件支持什么方法和選項?它有一些用戶需要注意的性能和可伸縮性問題嗎?如果用戶無法理解怎樣使用這個插件,他們很可能會找尋一個替代者。評論你的插件代碼也是很有益處的。目前這是你能提供給其他開發者的最好的禮物了。如果有人覺得他們可以很好的使用或者改進你的基礎代碼,那么你就是做了一件漂亮的工作。 維護的可能性 發布插件的時候,估計可能需要多少時間進行維護和支持。我們都喜歡與社區分享我們的插件,但是一個人需要對他回答問題,解決問題和持續改進的能力設置預期。只要前期簡單的在README文件中,為維護支持說明項目的意圖,就可以做到這一點。 ## 結論 在本章里,我們探索了幾個節省時間的設計模式,以及一些可以用來改進jQuery插件寫法的最佳實踐。有些更適宜于特定的使用案例,但我希望總體而言這些模式是有用的。 請記住,當選擇一個模式的時候,重要的是聯系實際。不要僅僅因為某個插件模式的好處而使用它,而要投入時間理解底層的結構,并搞清楚它能否很好的解決你的問題,或者適應你嘗試創建的組件。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看