摘要:以為例構造函數的內容構造函數的內部一般會做以下幾個操作各種給內部對象設置屬性。為什么呢源碼做出了解釋。在里面會調用用戶傳入的回調函數并觸發事件表示已經同步了。整個的源碼事實上就是這兩組東西。
1. 開場
強烈建議一邊看著源碼一邊讀本文章,本文不貼大段代碼。源碼地址。
在寫backbone應用的時候,說實話,大部分的時間都是在寫這三個模塊的內容。關于這三個模塊的分析網上隨隨便便就能找到一堆還不錯的文章。但我希望能夠找到一條線索,能把各自模塊的內部機理整理清楚。就像前一篇文章中介紹的Events那樣。Events整個模塊其實就是通過一些外部的方法來修改內部對象的屬性,從而達到事件管理的目的。以一條線索來看待整個模塊,一切都清晰了然了。下面就開始了~
(最近要開學,要準備回學校上課,亂七八糟的東西很多,所以文章可能也會拖一陣子啦...但是我還是非常希望能夠寫下來,半途而廢的感覺真心不好...
這一篇文章主要講backbone的Model, Collection和View。這三個模塊有很多相似的地方。這篇文章不會把模塊的每一個方法都介紹一遍,因為只要看源碼就知道,其實主要的方法只有幾個,而很多其他的模塊實際上只是在調用這幾個核心的方法而已。
2. Model & Collection & View首先講一下三者的相似之處。這一節讓我們來看看這三個模塊一個總體結構。
這三個模塊在結構上和Events不同。他們先通過以下方式來定義構造函數。(以View為例)
var View = Backbone.View = function(options) { // 構造函數的內容 };
構造函數的內部一般會做以下幾個操作:
各種給內部對象設置屬性。(各種this.a = b)
調用preinitialize
this.preinitialize.apply(this, arguments);
調用initialize
this.initialize.apply(this, arguments);
各個模塊的方法和屬性是通過underscore的extend來獲得的。注意在extend新加入的方法和屬性中,以下劃線開頭的變量是內部函數名。(其實理論上用戶也可以調用這些方法,誰叫Javascript沒有內部變量呢...)這些內部方法是供自己模塊內部調用的。
_.extend(View.prototype, Events, { // 這里是各種對View.prototype的拓展,定義各種方法 });
還有一個比較大的共同點,就是slient參數。這個參數決定了是否要trigger一個事件,在源碼用占了很大的篇幅對其進行分類討論。
3. Model 3.1 關鍵方法有一些關鍵的方法一進入函數就會根據傳入的參數的形態進行變化。因為backbone一些方法支持兩個參數傳入或者一個數組傳入,這時候需要有個判斷。
3.2 setset方法在model里面是個很不好理解的東西,看了網上大多數解析感覺都很模糊(而且遇到難理解的就用一些借口蒙混過去)。不得不說set里面復雜精妙程度是每讀一遍驚嘆一遍。
我想以變量的角度來講解可能是一個比較好的角度。
changing和this._changing
如果這個函數只是從頭執行到尾,那說實話,這兩個變量沒有任何意義。因為他們的值是確定的。看函數開頭:
var changing = this._changing; this._changing = true;
在函數結尾:
this._changing = false;
這個changing將永遠永遠是false。我上網看到有人說可能是webWorker,多線程相關的東西,但我直接在源碼console的時候卻發現,這個changing是會變的,而且我用得是todo范例。todo范例沒有任何類似webWorker的東西。這個假設猜測應該來說是不正確的。(不過這篇文章講得也很不錯啊)
所以這個changing到底有什么用呢?答案就是遞歸函數。set里明明沒有遞歸啊?其實遞歸藏在了所有trigger的事件的回調函數里面。源代碼下面的這一段:
// You might be wondering why there"s a `while` loop here. Changes can // be recursively nested within `"change"` events. while (this._pending) { options = this._pending; this._pending = false; this.trigger("change", this, options); }
這一個while里的trigger使得函數發生遞歸,然后重新調用set。這樣的話,下一次changing就等于true了,這個變量的作用才能發揮。可以看一下這個鏈接里面的講解。
current變量是用來作為引用改變attributes的,其實是set能設置attributes的本質。
changes數組是用來存放改變了的key的,用于后期的事件觸發。
changed & _previousAttributes
把這兩個放到一起是因為他們的一個特殊的地方。我在todo的主函數的render里面console,發現不論我做什么操作,changed === {},_previousAttributes沒有發生改變。后來在查看官方文檔的時候,才了解previous的用法:
var bill = new Backbone.Model({ name: "Bill Smith" }); bill.on("change:name", function(model, name) { alert("Changed name from " + bill.previous("name") + " to " + name); }); bill.set({name : "Bill Jones"});
set方法在被調用的時候,previous只有在回調函數里才能有用,也就是說,在回調函數外面想要用這個previous獲取前一個值是不可能的。它只能獲取到當前值。為什么呢?源碼做出了解釋。當用戶做出操作需要用到set方法的時候,其實set方法并不是直接執行完就結束了。在這個方法里面觸發了很多的事件,而previous只有在函數里觸發了的事件的回調函數“里面”才能返回正確的“前一個值”。changed也同理,因為不論中間如何變化,遞歸,到最后它會被設置為{}。
3.3 savesave方法的作用是把當前model的狀態保存到數據庫中,因此不可避免地要用到ajax。由于backbone已經有了一個封裝好的方法sync用于觸發ajax,因此在save當中重點是設置參數。需要設置的有success,error,method。
在success里面會調用用戶傳入的回調函數并觸發sync事件表示已經同步了。
error用封裝好的wrapError函數,這個函數用得很多,用于處理錯誤。
method根據實際要用那種方法設置
其中比較值得注意的是wait參數。這個參數會影響頁面更新的時機。如果wait是true的話,就會需要等到服務器端相應才更新頁面,否則就會立即更新。
destory方法也是與ajax有密切聯系的。主要也是設置ajax參數。它分了幾種不同的情況并作出了相應的處理:
wait是false,不用等待。發起delete請求,觸發內部函數destory。
wait是true,發起ajax,等待服務器響應才觸發destory更新頁面。
這是一個新的model,那就不需要發起請求了。
3.5 isValid驗證函數,通過調用內部函數_validate,在通過這個函數調用validate函數。然后返回一個錯誤,如果沒有錯誤就返回true,否則觸發invalid,返回false。
4. CollectionCollection類似一個數組,里面存放著各種以model為結構的對象。在Collection中也有這形式的判斷,如果傳入的參數是單個對象就會被轉換成數組。
4.1 set這是Collection的一個很常用的方法,源碼中這一段很長,也有點繁瑣,但是沒有特別難以理解的地方。整個set的結構是:
設置幾個數組(下面會詳細講)
設置實際的models(修改this.models)
trigger事件
主要來說就是有如下幾個關鍵點:
如果不符合model形式,轉換之。
設置相應的插入位置at。
設置set數組。set數組在里面作用是為給后面排序做準備。里面存放的是新的Collection的models。
設置toAdd數組。這個數組是用于存儲新建的合法的model,然后需要調用內部函數_addReference設置索引于_byId數組,并且添加all事件(后面就可以通過model直接trigger事件)。當slient不是true,后期可以通過遍歷它來觸發add事件。
設置toMerge數組。當這個model是原本已經存在的model的時候(cid匹配),就會修改,然后被push進這個數組中。
設置toRemove數組。然后通過內部函數_removeModels刪除那些已經不在set里面的models。
修改this.models,分兩種情況,一種是直接整個替換掉,一種是后面再添加。
如果silent不是true就要觸發事件。特別值得注意的一點是:這里面的事件有兩種,一種事件是由Model發出的,一種事件是有Collection發出的。從Model發出的事件可以很容易_addReference函數中發現
model.on("all", this._onModelEvent, this);
在這里注冊了,調用的是_onModelEvent函數。而其他沒有注冊的函數應該是給使用者注冊監聽用的。
4.2 sortsort所依據的是用戶傳入的comparator參數,這個參數可以是一個字符串也可以是一個函數,如果是字符串就通過underscore的sortBy方法,如果是個函數就直接傳入sort的第二個參數中。
4.3 fetch & createfetch和create是backbone與服務器端交互的一個接口。兩個方法內部處理其實都很好理解,就是設置ajax參數。最終本質上都是觸發sync。但是唯一不同的是fetch是通過自身的sync函數,但create是通過調用model的save,然后觸發sync的。在
model.save(null, options);
跟著這個save函數里面走,就會發現參數null傳入是有意義的。在save里面的參數設置會很好地賦值并最后觸發sync,而且有一個很有趣的點,就是這個create把model傳上服務器,但是這個model是一個相對獨立的狀態,僅僅通過它的Collection屬性來維系和Collection的關系。那就要求后端需要把這一個model添加到相應的Collection數據里面去。
4.4 reference在Collection有一個值得關注的內部變量,那就是_byId,這個變量用cid和id(所以model是一對一對出現的)來存儲Collection里面的model,方便直接性的存取。在源碼中有很多操作目的就是刪除,增加,獲取這個內部變量的值。
4.5 CollectionIterator這東西我覺得很有意思...在官方文檔里面沒有提到,但是由于涉及到ES6的東西所以覺得有點眼前一亮的感覺(哈哈哈),backbone在這里用了Symbol.iterator,具體用法在這個鏈接里有介紹,還是挺清晰的。通過設置CollectionIterator的Symbol.iterator和next方法。它通過內部變量_kind來區分種類,_index來確定對應的next的結果,這個對于寫迭代器還是有點借鑒意義的~
5. View在寫backbone應用的時候,View寫著寫著會越來越大...追根溯源,就是View的代碼很少...(大霧)。關于View,在寫相關代碼的時候有一些值是需要設置的(可選的)。下面的代碼就展示了可設置的參數,這些參數在View的方法中會用到(如果有的話)。
var viewOptions = ["model", "collection", "el", "id", "attributes", "className", "tagName", "events"];
下面我會從兩個大的方面來解讀源碼,一個是element,一個是Events。整個View的源碼事實上就是這兩組東西。
5.1 ElementView字面意思是視圖,而在瀏覽器中,視圖就是html所呈現的頁面。每一個View事實上就對應著html的一個元素(當然這個元素里面可以有很多很多元素)。這個元素默認標簽是div。與元素相關的代碼其實很簡單,首先要認清this.el和this.$el。前者是真正的節點,后者則是jquery對象的節點。后者由于是jquery式的,因此就可以做相關的jquery的操作。因此事件發起,刪除節點,設置屬性的操作都是jquery的api對this.$el或其子節點的操作。在進入構造函數的時候會調用一個叫_ensureElement的內部函數,在這個函數里會根據用戶設置的參數去構建節點,最后展現到頁面之上。
5.2 Events事件是View中非常重要的組成。這是用戶可以操作數據的一個接口。在View里面和數據相關的方法有delegateEvents,delegates,undelegateEvents,undelegate。里面通過使用者設置的events屬性來創建各種事件,操作各種事件。
{ "mousedown .title": "edit", "click .button": "save", "click .open": function(e) { ... } }
events相關代碼很簡單,但是有一個非常非常巧妙的地方:就是作者用了jquery事件相關api的命名空間。在delegate被調用的時候就給事件加上了一個特定的命名空間。
delegate: function(eventName, selector, listener) { this.$el.on(eventName + ".delegateEvents" + this.cid, selector, listener); return this; }
因此在后續需要對整體的所有事件進行操作的時候就會方便很多很多。
6. 最后的話這次源碼解析不能百分百保證是正確的,有一些混雜了自己的思考。因為不想像其他大部分的源碼解析那樣,對于問題模糊處理。但我覺得還是有意義的,因為每個人讀的角度不一樣。兼聽則明,也希望讀者能夠包容,希望深刻理解backbone的讀者也請多讀幾篇文章,多讀幾遍源碼。下一篇文章要寫router & history,這一個模塊可以多帶帶拆出來作為SPA的一個入口,個人認為這部分時backbone的backbone(骨架)。
希望能夠堅持更下去吧,開學了,事情也開始多了起來...
本人還是backbone小白,如果哪里說錯了或者怎樣,請輕噴~相互學習~
下面是全部的文章:
基于 Backbone + node 的個人簡歷生成器(個人學習總結)
Backbone源碼解讀(一)
Backbone源碼解讀(二)
Backbone源碼解讀(三)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87893.html
摘要:事件關于路由觸發事件是通過兩個函數來完成的,它們分別是和前者會檢測路由是否發生了改變,如果改變了就會觸發函數并調用函數,而后者會通過路由片段來找到相關的事件函數來觸發。 注意:強烈建議一邊閱讀源碼一邊閱讀本文。 終于到了backbone源碼解讀的最后一篇,這一篇和前面幾篇時間上有一定的間隔(因為要回學校有一堆亂七八糟的事...)。在這一篇里面會講解Bakcbone的sync & rou...
1. 開場 1.1 MVC? MVC是一種GUI軟件的一種架構模式。它的目的是將軟件的數據層(Model)和視圖(view)分開。Model連接數據庫,實現數據的交互。用戶不能直接和數據打交道,而是需要通過操作視圖,然后通過controller對事件作出響應,最后才得以改變數據。最后數據改變,通過觀察者模式更新view。(所以在這里需要用到設計模式中的觀察者模式) 1.2 Smalltalk-80...
摘要:接受個參數,包括事件的名稱,回調函數和回調函數執行的上下文環境。保留回調函數在數組中取出對應的以及中的函數。當然,你同樣可以在綁定的回調函數執行前手動通過將其移除。 Backbone源碼解讀 Backbone在流行的前端框架中是最輕量級的一個,全部代碼實現一共只有1831行1。從前端的入門再到Titanium,我雖然幾次和Backbone打交道但是卻對它的結構知之甚少,也促成了我想讀...
摘要:個人認為,讀懂老牌框架的源代碼比會用流行框架的要有用的多。另外,源代碼中所有的以開頭的方法,可以認為是私有方法,是沒有必要直接使用的,也不建議用戶覆蓋。 寫在前面 backbone是我兩年多前入門前端的時候接觸到的第一個框架,當初被backbone的強大功能所吸引(當然的確比裸寫js要好得多),雖然現在backbone并不算最主流的前端框架了,但是,它里面大量設計模式的靈活運用,以及令...
摘要:個人認為,讀懂老牌框架的源代碼比會用流行框架的要有用的多。另外,源代碼中所有的以開頭的方法,可以認為是私有方法,是沒有必要直接使用的,也不建議用戶覆蓋。 寫在前面 backbone是我兩年多前入門前端的時候接觸到的第一個框架,當初被backbone的強大功能所吸引(當然的確比裸寫js要好得多),雖然現在backbone并不算最主流的前端框架了,但是,它里面大量設計模式的靈活運用,以及令...
閱讀 4095·2023-04-26 01:48
閱讀 3267·2021-10-13 09:40
閱讀 1746·2021-09-26 09:55
閱讀 3633·2021-08-12 13:23
閱讀 1791·2021-07-25 21:37
閱讀 3436·2019-08-30 15:53
閱讀 1399·2019-08-29 14:16
閱讀 1400·2019-08-29 12:59