Backbone設計思路和關鍵源碼分析

一. Backbone的江湖地位:前端

backbone做爲一個老牌js框架爲大規模前端開發提供了新的開發思路:前端MVC模式,這個模式也是前端開發演變過程當中的一個重要里程碑,也爲MVVM和Redux等開發思路奠基了基礎,後來的react,vue無不是在backbone的影響下開創出來的經典模式。爲何這麼說呢?咱們先來回顧下Web前端開發的大概演變流程,本過程純粹我的理解,拋磚引玉,共同探討,若有誤差請看官指出錯誤:vue

1. 無前端:最先的網頁就是HTML,還只是靜態頁面。世界上第一個網頁:python

 

2. 萌芽:因爲用戶體驗問題(好比文本校驗等等),開發人員把服務端腳本(好比python,perl)等語言搬到到瀏覽器端,誕生了JavaScript,讓瀏覽器能夠分擔文本校驗等一部分任務,而不須要請求後臺服務器,這在當時網絡只有幾十KB的環境下,用戶體驗效果提高很是明顯。react

3. 混沌:這個時候各個網站爲了提高用戶體驗,豐富網站能力,開始了各類各樣的嘗試,ASP,PHP,JSP等等各類模式迅速成長起來,服務器腳本和JS腳本共同協做把數據展示在頁面中,處理用戶交互請求,頁面效果和能力獲得大大提高,互聯網在這個時候開始日新月異的發展起來。然而此時的架構只是單單解決了功能需求,卻沒有考慮系統的維護性,擴展性和性能,安全等各個方面,先後端還沒有分離,還處於混沌階段。那個時候尚未Web前端開發崗位,只有PHP,ASP或者JSP開發人員。ajax

  

 

4. 後端MVC:因爲項目的日漸擴大,系統的複雜度也日漸加深,Web程序愈發臃腫,維護性愈來愈成問題,因而出現了後端MVC模式,好比Structs、Spring MVC,ASP.NET等等,這種模式把服務端邏輯管理起來,以Model,View,Controller的模式來開發,將數據,視圖和操做邏輯分離開來,大大提升了代碼的清晰度和可維護性,讓以前的混沌局面有了很好的改觀。然而此時此刻先後端仍沒有分離,HTML,JS,後臺腳本語言(PHP,JSP,ASP,ASP.NET)等等仍然混雜在一塊兒,當需求頻繁變化時,視圖和邏輯修改仍然不能徹底分離。正則表達式

 

 

5. Ajax:後來就是出現了大名鼎鼎的ajax以及各類對應框架,讓先後端獲得了分離,前端處理UI展示,用戶交互,後臺處理數據查詢,更新,後臺邏輯計算等等。今後Web前端開發誕生,部分開發人員開始專一於Web前端領域,開始了歷史上的分久必合合久必分的"分"。此時不少邏輯被劃分到瀏覽器端,前端js的代碼規模開始日漸複雜起來,這時候出現不少經典框架好比大名鼎鼎的jQuery,其高速處理DOM,屏蔽瀏覽器兼容性,事件封裝,ajax封裝等功能確實很是好用。json

6. 模塊化:隨着js代碼愈來愈複雜,規模愈來愈大,擴展性和維護性又出如今開發人員面前,同時因爲js語言的特性,沒有模塊或者說包的概念,致使js語言容易引發變量衝突,腳本依賴衝突等等,代碼複用率低等等問題,爲了解決這些問題,Web前端進入到了模塊化開發時代,這個時代英雄輩出,包括YUI,AMD,CMD等等(requireJs,seaJs),在這個階段js代碼被模塊化起來,代碼的維護性,複用度獲得了極大的提高。後端

 

 

7. 前端MVC:模塊化開發雖然解決了純邏輯模塊的代碼維護性問題,可是Web開發畢竟是處理用戶界面操做,不可避免的要處理各類UI視圖問題,而js必需要和HTML,數據進行緊密的配合,而這些代碼依然是混雜狀態,最多見的場景就是js去操做各類DOM,HTML模版等等,代碼的耦合度很高,維護性很低。自此BackboneJS、EmberJS、KnockoutJS、AngularJS的出現,讓這些問題有了改觀,經過將後端MVC模式引入到前端,將前端邏輯也劃分爲Model,View,Controller等模式,好比今天的主角Backbone,他定義了Model,Collection,View等模式,把代碼的邏輯規範起來,有了必定的套路,極大的提升了開發效率,下降了代碼耦合性,這裏特別說明一下,因爲Web開發的特殊性,Contoller是沒法徹底從HTML分離開來的,因此在Backbone中,沒有獨立的Contller模塊,而是在Model和View,還有Router中體現出來,Model,View和Router均可以發起Controller邏輯操做。在這個階段,前端大規模開發終於有了本身的套路。數組

  

 

8. MVVM:MVVM是MVC在View層的一個優化,因爲MVC開發過程當中,View層會出現不少DOM的查詢,操做等等,很是繁瑣複雜,DOM性能也出現瓶頸,同時因爲HTML標籤的侷限性,UI層面的開發比較繁瑣,爲此Vue和React提出了MVVM的模式,在這個模式下開發人員完全拋棄DOM,以數據和View的綁定式開發,大大減小了Web開發中DOM操做的代碼量,同時提出了虛擬DOM的模式,必定程度上也提高了DOM操做的性能,其次,提出了全組件式開發,豐富了HTML標籤的能力,經過建立組件的方式,建立了新的HTML標籤,以組件的方式來開發Web頁面,從而下降了頁面代碼的耦合度,提高了Web開發的效率。瀏覽器

  

 

9. Redux/Vuex:在大規模Web開發過程當中,僅有MVVM是不夠的,MVVM只是UI層面的框架,並無包含邏輯層面,另外兩個問題還須要解決:代碼組織結構和組件通訊。MVC模式也是爲了解決前端複雜度問題,可是仍然不是最佳方案,試想如下的情景:

  

經過Controller來驅動Model和View的邏輯,當Model和View有成千上百的規模的時候,你會發現,這裏的邏輯觥籌交錯,有時候並不能很明確的知道究竟是哪裏改變了當前的視圖,同時當一個數據發生變化之後,又會有多少個View會發生變化,徹底不可預測,甚至失控。爲此開發人員借用了Flux的開發思路,提出了Redux的開發模式,他提出三大原則,在這個模式下,數據單向流動對UI產生影響,讓開發人員能夠清晰的看到數據的流向,開發人員只對數據進行操做,View由數據發生變化而刷新,從而使整個邏輯的流向很是清晰可追溯,可控。

10. Node全棧時代:"話說天下大勢,分久必合,合久必分",隨着先後端分離多年以後,Node的出現讓全棧開發又從新回到了前端開發的視野,Web開發人員不甘於僅限在瀏覽器端折騰,V8的出現讓開發人員能夠經過js語言在服務器端開發,至此,是否是說Node又要一匡天下了呢?

  

 

 二. Backbone介紹

好了廢話很少說,到這裏你應該知道BackboneJs在歷史中的來歷和地位了吧?雖然Backbone有着本身的優缺點,並且如今已經再也不是Backbone風光的時代了,可是瞭解Backbone的設計思想和源碼結構,仍是對咱們平常開發仍是很是有幫助的。咱們首先看幾個問題

1. backbone是什麼?backbone帶來了什麼?爲何要使用backbone?

(1)首先backbone是一個前端MVC框架,爲大型Web程序提升了一個骨架的做用,讓開發人員更好的組織和設計代碼,他須要跟understore和jQuery一塊兒配合。

(2)backbone帶來了MVC模式,分離了UI和數據,帶來了事件通訊機制,帶來了單頁面開發的便捷方式。沒有MVC以前的粗獷模式有哪些問題:

a) 數據保存在內部變量中和業務模型無關,雜亂零散不可控

b) 代碼複用率低,模塊化後有些改觀,但沒法從業務層面複用

c) 代碼結構耦合在一塊兒,不清晰,維護成本高

d) 需求變化時,因爲維護成本居高不下,更新需求的開發效率底下。

e) 單頁面開發比較繁瑣

而MVC模式從必定程度上改觀了上述問題。

(3)當你的項目中不少頁面屬於單頁面模式,而且有不少DOM操做,數據的查詢,修改等等,你可使用backbone更好的組織代碼,提升開發效率,下降維護成本。

2. 他有什麼優勢?

(1)View能夠劃分UI,UI的複用性更高,增長UI的內聚度,下降耦合

(2)View和Model事件機制進行通訊,組件更加獨立

(3)MVC架構讓代碼結構清晰可維護

(4)CRUD 比ajax請求更加便捷

(5)單頁面開發更加方便

3. 他有什麼缺點?

(1)Model設計比較簡單,沒法知足複雜數據關係,如多對多的數據關係等等

(2)寫代碼須要當心,避免內存泄漏問題

4. 設計思路是什麼?

Backbone的設計思路,就是把UI分爲View來定義,數據分爲Model來定義,多個Model能夠保存在Collection,在Model,Colletion和Router中實現CURD操做,經過事件驅動來更新View,主要有3種事件驅動

(1) 瀏覽器DOM事件:綁定在DOM中的瀏覽器事件

(2) 模型事件:Model和Collection都有save,change等事件

(3) 路由事件:路由變化事件

最終的效果就是用MVC模式來管理組織代碼。

 

咱們能夠看到上面的流向和React和Vue相比其實仍是有些雜亂的,但在當時和jQuery雜亂綁定DOM的相比已是進步不少了。

三. backbone源碼解析

Backbone源碼其實很是簡單,直接比對源碼閱讀便可。簡單說明下,總共有8大模塊,每一個模塊都設計的很是規範:

1. Event事件模塊:一個事件處理模塊

(1) on:註冊事件回調函數,若是傳人是json對象或空格分隔的多個事件,經過eventsApi方法遍歷處理

(2) once:註冊只執行一次的回調函數

(3) off:刪除事件回調函數,若是沒有指明事件名,刪除全部事件的回調列表,若是沒有指明回調函數,刪除該事件的全部回調函數,若是都有指定則刪除指定的事件回調函數。若是傳人是json對象或空格分隔的多個事件,經過eventsApi方法遍歷處理

(4) trigger:觸發指定的事件,執行該事件的回調函數列表,若是該對象有all事件,則也會觸發all事件。若是傳人是json對象或空格分隔的多個事件,經過eventsApi方法遍歷處理

(5) listenTo:把事件和回調對象綁定到指定的對象上去。

(6) listenToOnce:同listenTo,只是只會執行一次

(7) stopListening:將對象的事件對象off掉

(8) eventsApi:若是傳入的事件是json對象或空格分隔的多個事件,遍歷json對象或多個事件,挨個執行指定的操做。

 

2. Model模型:數據模型,包括數據的設置,獲取,後臺服務的增刪查改等操做

屬性:

(1) cid: 當前數據模型的惟一ID,由underscore庫生存

(2) attributes:當前數據模型的屬性

(3) collection:數據模型所屬的集合對象

(4) changed:保存了屬性值發生變化的對象集合

方法:

(1) 構造函數: 建立cid,設置collection,若是有parse方法就作parse轉換,最後把屬性參數設置到本數據模型中,調用initialize方法。

(2) initialize:一個空函數,若是你須要在初始化進行操做能夠重寫這個方法。

(3) get:或者某個屬性的值

(4) set:設置屬性值,並將值發生變化了的屬性保存到changed對象中去

(5) has:判斷數據模型是否包含某個屬性

(6) clear:清空數據模型的屬性

(7) fetch:經過ajax請求獲取模型的屬性值

(8) save:保存模型值到後臺,若是設置了wait參數,必需要後臺返回成功才能更新本地數據

(9) destory:刪除數據模型值,若是設置了wait參數,必需要後臺返回成功才能更新本地數據

最後將understore庫的'keys', 'values', 'pairs', 'invert', 'pick', 'omit', 'chain', 'isEmpty'方法添加到Model對象中。

 

3. Collection集合:Model對象的集合,包括對數據模型的添加,刪除,設置,增刪查改等操做

屬性:

(1) model:Mode對象

(2) models:Model實例集合數組

方法:
(1) 構造函數: 初始化參數,設置model對象,初始化models數組,調用initialize方法,若是傳入了model對象數組,清空原數組的依賴關係,將原數組保存到option中去,將新的model數組添加到models數組中去。

(2) initialize:一個空函數,若是你須要在初始化進行操做能夠重寫這個方法。

(3) add:添加一個Model對象到本集合中去(也就是models數組)

(4) remove:刪除一個或多個Model對象,清理_byId對象,清理依賴關係等等

(5) set/get:get獲取指定的model對象,set添加model對象到models數組中去,添加新的對象,合併已經存在的對象,刪除未列出的對象,

(6) push/pop:添加或刪除model對象到models數組尾部

(7) shift/unshift:添加或刪除model對象到models數組頭部

(8) where:查找指定屬性的model對象集合

(9) fetch:從後臺讀取Model數據

(10) create:建立一個新的model數據,並保存到後臺中去

最後將understore庫中的全部數組的方法和排序方法添加到Collection對象中去。

 

4. View模型: 視圖模塊,對UI中的DOM元素進行操做,並註冊瀏覽器事件

 

屬性:

(1) cid:視圖id

(2) el:視圖所在的DOM對象

(3)$el:視圖所在DOM對象的jQuery對象

(4)id:視圖所在DOM對象ID屬性

(5)className:視圖所在DOM對象className

(6)events:綁定到該視圖的對象列表

方法:
(1) 構造函數: 設置cid,初始化option,調用_ensureElement和initialize,其中_ensureElement的做用是確保el是存在的,若是不存在建立一個DOM對象,而後將該對象設置爲el,jQuery對象設置爲$el,刪除el的全部原事件,綁定本視圖的event對象列表,若是存在直接將該對象設置爲el,後面的邏輯同上。

(2) initialize:一個空函數,若是你須要在初始化進行操做能夠重寫這個方法。

(3) render:視圖渲染邏輯,須要用戶重寫

(4) remove:刪除視圖,將刪除視圖的DOM對象和事件邏輯

(5) setElement:將某個DOM對象設置爲el,並綁定事件對象

(6) delegateEvents:爲el添加事件對象

(7) undelegateEvents:爲el刪除事件對象

 

5. Router路由模塊:主要定義和處理路由邏輯,配合History對象能夠實現路由規則和url的映射,實現後臺和前進按鈕的單頁效果,Router路由模塊的主要做用就是記錄用戶定義的路由規則,做爲History的對外接口。例如:

var myRouter = Backbone.Router.extend({
  routes: {
    "help":                 "help",    // #help
    "search/:query":        "search",  // #search/test
    "search/:query/p:page": "search"   // #search/test/p2
  },
  help: function() {
    ...
  },
  search: function(query, page) {
    ...
  }
});

a) help對於help方法

b) search/:query傳遞一個參數

c) search/:query/p:page 傳遞兩個參數,第二個參數略去p

d) "file/*path"匹配file/後的全部路徑

e) "search/:query(/:page)"能夠匹配#serach/query和 #serach/query/p1,第一種狀況,傳入 "query" 到路由對應的動做中去, 第二種狀況,傳入"query" 和 "p1" 到路由對應的動做中去

屬性:

(1) routes:路由列表

方法:
(1) 構造函數: 初始化路由列表,調用_bindRoutes和initialize。

(2) initialize:一個空函數,若是你須要在初始化進行操做能夠重寫這個方法。

(3) _bindRoutes:將路由列表綁定到實例中去

(4) route:綁定單個路由,首先判斷是不是正則表達式,不是的化調用_routeToRegExp轉換,首先添加到history對象的handler數組中,而後添加回調函數,回調函數中回調callback,並分析路徑中的參數傳入到callback中。

(5) execute:執行某個路由的回調,每當路由和其相應的callback匹配時被執行,能夠被改寫自定義包裝或解析路由

(6) navigate:調用history對象的navigate方法,將路由保存到history中去,或者設置trigger:true跳轉url,設置replace:true則只跳轉,不保存歷史。

(7) _routeToRegExp:將字符串轉換爲路由正則表達式

(8) _extractParameters:分析路徑中的參數,好比search/:query/p:num中的query和p

 

6. History歷史記錄管理模塊:配合路由進行歷史記錄管理,從而能夠實現瀏覽器的後退前進操做,對於高級瀏覽器使用History API,對於舊瀏覽器實現兼容,Backbone實現了3種單頁面效果,實現了優雅兼容:

a) pushState:若是設置選項pushState爲true而且瀏覽器支持,則開啓該模式,該模式的好處是,既能實現相似ajax方式的無刷新url的更新,也能實現url的變化,對用戶來講交互體驗最好

b) hasChange:若是設置hashChange選項爲true而且瀏覽器支持,則開啓該模式,該模式經過修改url的hash值而且監控hashChange事件來實現單頁面效果,能夠實現無刷新更新,可是url是經過hash來區分的,體驗略差

c) url跳轉:若是瀏覽器既不支持pushState也不支持hashChange事件,backbone只好使用了url跳轉的方式來實現更新,建立一個隱藏的iframe來記錄上次的url的hash,建立一個定時器來定時比較二者hash是否更新,體驗不太好

全部這些變化都僅僅是體如今url和history歷史記錄中去,真正要更新頁面的邏輯是路由定義的回調方法。

屬性:

(1) handlers:路由規則列表

(2) location:window.location對象

(3) history:window.history對象

方法:
(1) 構造函數: 初始化路由列表,綁定checkUrl方法內部對象爲History對象。

(2) atRoot:當前url是否爲root根路徑

(3) getSearch:獲取參數部分

(4) getHash:獲取hash部分

(5) getPath:獲取路徑和全部參數部分,若是option指定了root根路徑,則在url中去掉該root

(6) getFragment:獲取url中的片斷,若是須要pushState或者不支持pushState的瀏覽器須要刷新url,則調用getPath,不然調用getHash

(7) start:開始監控路由,有兩個選項:pushState模式經過pushState地址到history中去,監聽popstate事件來實現單頁面效果,若是是hasChange模式,經過更新hash和監聽hashchange事件來實現,若是是老舊的瀏覽器,建立一個隱藏iframe記錄上次的url,建立一個定時器定時比較當前url和iframe的url是否變化來以爲是否跳轉。

(8) stop:中止監控popstate或hashchange方法,若是是iframe模式刪除iframe,刪除定時器

(9) route:將路由和回調函數添加到handler數組中去

(10) checkUrl:判斷當前url是否發生了變化,若是是則調用loadUrl方法

(11) loadUrl:遍歷路由列表,找到符合當前url的路由規則並執行回調

(12) navigate: 執行單頁面的歷史記錄管理,若是是pushState模式,則調用replaceState或pushState來管理歷史,若是hasChange模式,則經過更新url的hash管理歷史,不然直接跳轉url,經過瀏覽器本身管理歷史

 

7. Sync異步請求:ajax請求模塊,主要是對ajax的配置,最終調用的是Backbone.ajax方法,若是有jQuery調用jQuery的ajax方法,能夠被改寫。

 

8. extend擴展函數:返回一個新的對象child,將實例對象和靜態對象拷貝到目標對象中去,經過寄生組合繼承方式將child繼承自實例對象,經過understore庫的extend方法將靜態對象拷貝到child中去,而後返回child對象。

相關文章
相關標籤/搜索