Backbone.js是什么?
Bakcbone.js 是一個(gè)JavaScript MVC框架,提供了良好的代碼組織能力,可以方便地將應(yīng)用程序解耦成可以復(fù)用的部分,為建立大型的單頁面應(yīng)用提供框架支持,目前的版本是 0.9.10(注:現(xiàn)在已到1.2.1版本)。通過將應(yīng)用程序分解成MVC模式中不同職責(zé)的模塊,帶來了以下幾點(diǎn)好處。
1、降低維護(hù)成本。數(shù)據(jù)、控制器、視圖的更新都是獨(dú)立進(jìn)行的,互相之間都是松散耦合的。
2、解耦數(shù)據(jù)和視圖,便于直接對(duì)業(yè)務(wù)邏輯進(jìn)行單元測試。
3、便于團(tuán)隊(duì)合作,負(fù)責(zé)UI開發(fā)和業(yè)務(wù)邏輯開發(fā)的工程師可以分工并行工作。對(duì)于Web前端開發(fā)來講,負(fù)責(zé)HTML模板和CSS的界面工程師和負(fù)責(zé)業(yè)務(wù)邏輯的JavaScript工程師可以協(xié)同工作。
Backbone.js算是比較輕量的MVC框架,所謂輕量,是說它只關(guān)注一個(gè)框架應(yīng)該關(guān)注的最基本的事情——如何給應(yīng)用分層、如何組織各種功能的代碼。至于在實(shí)際開發(fā)中需要用到的Utils或UI組件,Backbone.js則沒有提供任何支持。但Backbone.js所依賴的 Underscore.js是一個(gè)功能比較全面的非侵入式工具函數(shù)類庫,算是在Utils方面的一個(gè)補(bǔ)充。
輕量并不意味著功能薄弱。首先,Backbone.js的精髓是它定義前端MVC的方式和編碼哲學(xué),并依據(jù)這些規(guī)定了如何去給代碼分層,因此 Backbone.js能夠讓前端工程在可維護(hù)性和擴(kuò)展性上都得到質(zhì)的提升;同時(shí),由于其良好且易于理解的結(jié)構(gòu),各個(gè)模塊之間都是松散耦合的,雖然目前官方并沒有提供根據(jù)實(shí)際需求build文件的功能,但如果你愿意,完全可以自己手工刪掉源碼中的Bakcbone.View只使用Model和 Collection;最后,Backbone.js的任何一個(gè)部分都是非常容易擴(kuò)展的。因此,Backbone.js的功能實(shí)際上非常強(qiáng)大的。下面將介紹Backbone.js的主要組件(架構(gòu)如圖1所示)。
圖1 架構(gòu)圖
Backbone.Events和Backbone.Sync
Backbone.Events和Backbone.Sync兩個(gè)組件是Backbone.js異步通信、事件驅(qū)動(dòng)的編程模型的基礎(chǔ)。
Backbone.js中所有的組件都通過_.extend的方法“繼承”了Backbone.Events所提供的功能,可以維護(hù)一套自己的事件訂閱和回調(diào)列表。通過Events.on(event,[callback],[context])和Events.off([event], [callback], [context])兩個(gè)方法來實(shí)現(xiàn)對(duì)事件的訂閱和取消訂閱。
早期版本中的事件訂閱和取消訂閱是通過Events.bind和Events.unbind兩個(gè)方法實(shí)現(xiàn)的,目前的版本中還保留了這兩個(gè)別名方法,但不推薦使用。
Backbone.js的所有組件都有一些內(nèi)置的事件,可以查閱官方文檔。除了預(yù)置事件外,通過Events.trigger(event,[*args])方法也可以方便地觸發(fā)自定義事件。
從0.9版開始,Backbone.Events提供了Events.listenTo(other,event,callback)和 Events.stopListening([other],[event],[callback])兩個(gè)新方法來通過另外一種形式實(shí)現(xiàn)事件的訂閱和取消訂閱。
與on和of f不同,這種方式將監(jiān)聽的主動(dòng)權(quán)轉(zhuǎn)換了。舉個(gè)例子來說:有對(duì)象A和對(duì)象 B,B.on('someThingHappened',A.doSomeThing)是當(dāng)對(duì)象BsomeThingHappened時(shí)候知對(duì)象A去 doSomeThing;而A.listenTo(B,'someThingHappend',A.doSomeThing)是對(duì)象A主動(dòng)去盯著對(duì)象B,當(dāng)它someThingHappend的時(shí)候去doSomeThing。
第二種方式最大的意義是變被動(dòng)為主動(dòng),從而實(shí)現(xiàn)了IoC(Inversionofcontrol,控制反轉(zhuǎn))。監(jiān)聽者和被監(jiān)聽者之間沒有了耦合,只要被監(jiān)聽的對(duì)象能夠拋出指定的事件,就可以和監(jiān)聽者組合在一起,甚至不需要去關(guān)心被監(jiān)聽對(duì)象的類型,這對(duì)代碼的復(fù)用和行為抽象有很大的幫助。在測試層面,可以輕易地把被監(jiān)聽對(duì)象換成mock的測試代碼來模擬真實(shí)情況。
Backbone.Sync則將同服務(wù)器的通信封裝了起來,當(dāng)Collection和Model需要和服務(wù)器通信交換數(shù)據(jù)時(shí),會(huì)去調(diào)用 Backbone.Sync中對(duì)應(yīng)的方法并發(fā)送請(qǐng)求,如果服務(wù)器端支持RESTfulAPI就可以將整個(gè)通信過程描述得非常優(yōu)雅并易于擴(kuò)
Sync的實(shí)現(xiàn)可以是jQuery.ajax的封裝,也可以是其他的類庫提供的異步通信工具的封裝。
Backbone.Model和Backbone.Collection
Backbone.js中的Model和Collection共同構(gòu)成了MVC中的M層。Model的本質(zhì)就是一組以keyvalue形式保存的數(shù)據(jù),可以通過Backbone.Model.extend來定義自己的Model。
上面的示例代碼中defaults屬性定義了一組默認(rèn)值,當(dāng)Model初始化時(shí),如果沒有指定defualts中所定義的屬性的值,就會(huì)用默認(rèn)值來填充 Model;initialize方法會(huì)在Model被實(shí)例化時(shí)調(diào)用,用來進(jìn)行一些初始化的操作;validate方法會(huì)在Model的save或set 方法被調(diào)用時(shí)執(zhí)行,可以根據(jù)具體需求進(jìn)行擴(kuò)展。
通過Model的getters和setters可以實(shí)現(xiàn)對(duì)Model中屬性的讀寫,并且當(dāng)set方法被調(diào)用時(shí),Model會(huì)將屬性變化的事件廣播給所有訂閱者(通常是視圖),驅(qū)動(dòng)視圖重新渲染或其他關(guān)聯(lián)的Model的數(shù)據(jù)更新。
Collection是一組Model的集合,通過Collection可以將一組數(shù)據(jù)結(jié)構(gòu)相同的Model有序地組織在一起,進(jìn)行批量操作和管理等。同時(shí),Collection代理了Undersore.js中眾多用來操作Collection的工具方法,例如find、filter、map等。
Model和Collection都可以通過RESTfulAPI同服務(wù)器進(jìn)行數(shù)據(jù)交換。
Backbone.View
Backbone.View是基于Backbone.js開發(fā)的Web App中的核心部分,負(fù)責(zé)用戶交互事件的捕捉和處理、把用戶輸入導(dǎo)向Model或Collection、渲染視圖、操作DOM等。Backbone.js 的內(nèi)部實(shí)現(xiàn)依賴$變量,因此DOM操作的庫可以在jQuery、Zepto或Ender等中選擇。從目前的情況來看,在桌面瀏覽器中,Sizzle.js(jQuer y所使用的SelectorEngine)的性能還是甩開Zepto幾條街的,因此面向桌面瀏覽器的開發(fā)還是推薦使用jQuery,移動(dòng)端考慮到文件體積等因素推薦使用Zepto。
對(duì)于一個(gè)Backbone.View來講,最重要的就是$el屬性,$el是一個(gè)jQuery對(duì)象(取決于所采用的DOM操作類庫),是一個(gè)視圖的最外層容器。容器所采用的HTML標(biāo)簽可以通過tagName屬性來指定,可以是ul也可以是header或其他任何標(biāo)簽,默認(rèn)情況下是div。
容器內(nèi)部的DOM渲染可以通過模板引擎來完成。Underscore.js本身提供了一個(gè)_.template的方法,因此Backbone.js不需要額外的模板引擎支持。當(dāng)然,如果有特殊的需求,例如和后端共用模板文件,也可以選用Mustache等其他的模板引擎。
這樣一個(gè)View就被渲染到界面上了。上面的代碼中還監(jiān)聽了Model的change事件,當(dāng)數(shù)據(jù)發(fā)生變化時(shí),驅(qū)動(dòng)視圖重新渲染。當(dāng)Model的數(shù)據(jù)比較豐富時(shí),只有一個(gè)屬性變化就重新渲染整個(gè)視圖顯然會(huì)帶來性能上的隱患,因此這里的最佳實(shí)踐就是把render的過程break-down成粒度更細(xì)的片段。
值得注意的是,當(dāng)一個(gè)View不再被需要時(shí),一定要記得銷毀,除了銷毀DOM對(duì)象外,也要銷毀所有的事件監(jiān)聽器。在只有Events.on和 Events.off的年代,由于銷毀View時(shí)需要逐一取消訂閱所有的消息,經(jīng)常由于忘記解除某個(gè)綁定導(dǎo)致產(chǎn)生被稱為GhostView的垃圾對(duì)象,既無法被釋放也無法被回收。這也是Events.listentTo方法帶來的另外一個(gè)好處——只要調(diào)用Events.stopListening方法即可將此對(duì)象的所有事件監(jiān)聽器銷毀。
所有的DOM事件也是通過$el來代理的,在Backbone.View中可以通過以下方法來方便地管理DOM事件。
Backbone.js的內(nèi)部實(shí)現(xiàn)里,在View的構(gòu)造方法中調(diào)用View. initialize后將繼續(xù)調(diào)用View.delegateEvents方法,這個(gè)方法將解析events屬性所定義的事件和回調(diào)列表,并將全部事件代理到$el對(duì)象上。由于使用的是事件代理,某些不支持冒泡的DOM事件則必須另外監(jiān)聽,如滾動(dòng)條事件。
Backbone.Router和Backbone.History
Router是用來在URLHash和特定的動(dòng)作或視圖之間做映射的。
最后一句History.start是告訴Backbone.js開始對(duì)URLHash的變化進(jìn)行監(jiān)視,也可以隨時(shí)通過History.stop來停止監(jiān)視。同時(shí),如果目標(biāo)平臺(tái)是支持HTML5Histor yAPI的,那么在s