開始學習 Backbone
簡介
Web 應用程式越來越關注於前端,使用客戶端指令碼與 Ajax 進行互動。由於 JavaScript 應用程式越來越複雜,如果沒有合適的工具和模式,那麼 JavaScript 程式碼的高效編寫、非重複性和可維護性方面會面臨挑戰。模型-檢視-控制器 (MVC) 是一個常見模式,可用於伺服器端開發以生成有組織以及易維護的程式碼。MVC 支援將資料(比如通常用於 Ajax 互動的 JavaScript Object Notation (JSON) 物件)從表示層或從頁面的文件物件模型 (document object model, DOM) 中分離出來,也可適用於客戶端開發。
Backbone(也稱為 Backbone.js)是由 Jeremy Ashkenas 建立的一個輕量級庫,可用於建立 MVC 類應用程式。Backbone:
- 強制依賴於 Underscore.js,Underscore.js 是一個實用型庫
- 非強制依賴於 jQuery/Zepto
- 根據模型的變更自動更新應用程式的 HTML,有助於程式碼維護
- 促進客戶端模板使用,避免了在 JavaScript 中嵌入 HTML 程式碼
模型、檢視、集合和路由器是 Backbone 框架中的主要元件。在 Backbone 中,模型會儲存通過 RESTful JSON 介面從伺服器檢索到的資料。模型與檢視密切關聯,負責為特定 UI 元件渲染 HTML 並處理元素上觸發的事件,這也是檢視本身的一部分。
在本文中,我們將學習幾個不同的 Backbone.js 框架元件。研究 MVC 如何應用於 Backbone。通過這些例項,看看在建立 Ajax 應用程式或者單頁介面 (SPI) 時 Backbone 是多麼的重要。
下載 本文使用的原始碼。
SPI 應用程式:Backbone.Router
和 Backbone.history
含有大量 Ajax 互動的應用程式越來越像那些無頁面重新整理的應用程式。這些應用程式常常試圖限制與單個頁面的互動。該 SPI 方法提高了效率和速度,並使整個應用程式變得更靈敏。狀態概念代替了頁面概念。雜湊 (Hash) 片段被用於識別一個特定狀態。雜湊片段 是 URL 中雜湊標籤 (#) 後的那部分,是該類應用程式的關鍵元素。清單 1 顯示了一個 SPI 應用程式使用兩個不同的雜湊片段產生的兩個不同狀態。
清單 1. SPI 或 Ajax 應用程式中的兩個不同狀態
http://www.example.com/#/state1 http://www.example.com/#/state2
Backbone 提供一個稱為路由器(版本 0.5 前稱之為控制器)的元件來路由客戶端狀態。路由器可以擴充套件 Backbone.Router
函式,且包含一個雜湊對映(routes
屬性)將狀態與活動關聯起來。當應用程式達到相關狀態時,會觸發一個特定活動。清單 2 展示了一個
Backbone 路由器示例。
清單 2. Backbone.Router
示例:routers.js
App.Routers.Main = Backbone.Router.extend({ // Hash maps for routes routes : { "" : "index", "/teams" : "getTeams", "/teams/:country" : "getTeamsCountry", "/teams/:country/:name : "getTeam" "*error" : "fourOfour" }, index: function(){ // Homepage }, getTeams: function() { // List all teams }, getTeamsCountry: function(country) { // Get list of teams for specific country }, getTeam: function(country, name) { // Get the teams for a specific country and with a specific name }, fourOfour: function(error) { // 404 page } });
建立的每個狀態可以為書籤。當 URL 碰到類似下面情況時,會呼叫這 5 個活動(index
、getTeams
、getTeamsCountry
、getTeamCountry
和
fourOfour
)。
http://www.example.com
觸發index()
http://www.example.com/#/teams
觸發getTeams()
http://www.example.com/#/teams/country1
觸發getTeamsCountry()
傳遞country1
作為引數http://www.example.com/#/teams/country1/team1
觸發getTeamCountry()
傳遞country1
和team1
作為引數http://www.example.com/#/something
觸發fourOfour()
以作*
(星號)使用。
要啟動 Backbone,先例項化頁面載入的路由器,並通過指令 Backbone.history.start()
方法監視雜湊片段中的任何變更,如
清單 3 所示。
清單 3. 應用程式例項化(使用 jQuery)
$(function(){ var router = new App.Routers.Main(); Backbone.history.start({pushState : true}); })
當例項化路由器時,會生成 Backbone.history
物件;它將自動引用 Backbone.History
函式。Backbone.History
負責匹配路由和
router
物件中定義的活動。start()
方法觸發後,將建立 Backbone.history
的
fragment
屬性。它包含雜湊片段的值。該序列在根據狀態次序管理瀏覽器歷史方面十分有用。使用者如果想要返回前一狀態,單擊瀏覽器的返回按鈕。
在
清單 3 的示例中,通過一個啟用 HTML5 特性 pushState
的配置呼叫 start()
方法。對於那些支援
pushState
的瀏覽器,Backbone 將監視 popstate 事件以觸發一個新狀態。如果瀏覽器不能支援 HTML5 特性,那麼
onhashchange
活動會被監視。如果瀏覽器不支援該事件,輪詢技術將監視 URL 雜湊片段的任何更改。
模型和集合
模型和集合是 Backbone.js 的重要元件,模型將資料(通常是來自伺服器的資料)儲存在鍵值對中。要建立一個模型,需要擴充套件 Backbone.Model
,如
清單 4 所示。
清單 4. Backbone.Model
建立
App.Models.Team = Backbone.Model.extend({ defaults : { // default attributes } // Domain-specific methods go here });
App.Models.Team
函式是一個新模型函式,但是必須建立一個例項才能在應用程式中使用特定模型,如
清單 5 所示。
清單 5. 模型例項化
var team1 = new App.Models.Team();
現在,變數 team1
有一個名為 cid 的欄位名,這是一個客戶端識別符號,形式為 "c" 再加上一個數字(例如,c0、c1、c2)。模型是通過儲存在雜湊對映中的屬性來定義的。屬性可以在例項化時進行設定,或者使用
set()
方法設定。屬性值可通過 get()
方法檢索。清單 6 顯示瞭如何通過例項化或
get()
/set()
方法設定和獲取屬性。
清單 6. 模型例項化和 get/set 方法
// "name" attribute is set into the model var team1 = new App.Models.Team({ name : "name1" }); console.log(team1.get("name")); // prints "name1" // "name" attribute is set with a new value team1.set({ name : "name2" }); console.log(team1.get("name")); //prints "name2"
當使用 JavaScript 物件時,使用 set()
方法建立或者設定屬性值的原因並不是顯而易見的。其中一個原因是為了更新此值,如
清單 7 所示。
清單 7. 以錯誤的方法更新屬性
team1.attributes.name = "name2";
為了避免 使用
清單7 中的程式碼,使用 set()
是改變模型狀態並觸發其變更事件的唯一方法。使用 set()
提升封裝原則。清單 8 展示瞭如何將一個事件處理程式綁到發生變更的事件中。該事件處理程式包含一個 alert,在呼叫
set()
方法時會被觸發,如
清單 6 所示。但是,在使用
清單 7 中的程式碼時不觸發 alert。
清單 8. 更改 App.Models.Team 模型中的事件處理程式
App.Models.Team = Backbone.Model.extend({ initialize : function(){ this.bind("change", this.changed); }, changed : function(){ alert("changed"); } });
Backbone 的另一個優勢是易於通過 Ajax 互動與伺服器進行通訊。在模型上呼叫一個 save()
方法會通過 REST JSON API 非同步將當前狀態儲存到伺服器。清單 9 展示了此示例。
清單 9. 在模型物件上呼叫 save
方法
barca.save();
save()
函式將在後臺委託給 Backbone.sync
,這是負責發出 RESTful 請求的元件,預設使用 jQuery 函式
$.ajax()
。由於呼叫了 REST 風格架構,每個 Create、Read、Update 或 Delete (CRUD) 活動均會與各種不同型別的 HTTP 請求(POST
、GET
、PUT
和
DELETE
)相關聯。首先儲存模型物件,使用一個 POST
請求,建立一個識別符號 ID,其後,嘗試傳送物件到伺服器,使用一個
PUT
請求。
當需要從伺服器檢索一個模型時,請求一個 Read 活動並使用一個 Ajax GET
請求。這類請求使用 fetch()
方法。要確定匯入模型資料或者從中取出模型資料的伺服器的位置:
- 如果模型屬於一個
collection,那麼集合物件的
url
屬性將是該位置的基礎,並且該模型 ID(不是 cid)會被附加以構成完整的 URL。 - 如果模型不是在一個集合中,那麼該模型的
urlroot
屬性被用作該位置的基礎
清單 10 顯示瞭如何獲取一個模型。
清單 10. 模型物件的 Fetch()
方法
var teamNew = new App.Models.Team({ urlRoot : '/specialTeams' }); teamNew.save(); // returns model's ID equal to '222' teamNew.fetch(); // Ajax request to '/specialTeams/222'
validate()
方法被用於驗證模型,如
清單 11 所示。需要重寫 validate()
方法(在呼叫 set()
方法時觸發)來包含模型的有效邏輯。傳遞給該函式的惟一引數是一個 JavaScript 物件,該物件包含了
set()
方法更新的屬性,以便驗證那些屬性的條件。如果從 validate()
方法中沒有返回任何內容,那麼驗證成功。如果返回一個錯誤訊息,那麼驗證失敗,將無法執行
set()
方法。
清單 11. 模型的驗證方法
App.Models.Team = Backbone.Model.extend({ validate : function(attributes){ if (!!attributes && attributes.name === "teamX") { // Error message returned if the value of the "name" // attribute is equal to "teamX" return "Error!"; } } }
一組模型被分組到到集合中,這個集合是 Backbone.Collection
的擴充套件函式。集合具有一個模型屬性的特性,定義了組成該集合的模型型別。使用
add()
/remove()
方法可以將一個模型新增和移動到集合中。清單 12 顯示瞭如何建立和填充一個集合。
清單 12. Backbone 集合
App.Collections.Teams = Backbone.Collection.extend({ model : App.Models.Team }); var teams = new App.Collections.Teams(); // Add e model to the collection object "teams" teams.add(team1); teams.add(new App.Models.Team({ name : "Team B" })); teams.add(new App.Models.Team()); teams.remove(team1); console.log(teams.length) // prints 2
建立的 teams
集合中包含一個含有兩個模型的陣列,儲存在模型屬性中。儘管,在典型 Ajax 應用程式中,會從伺服器動態(不是人工)填充該集合。fetch()
方法可以幫助完成此項任務,如
清單 13 所示,並將資料儲存到模型陣列中。
清單 13. Fetch()
方法
teams.fetch();
Backbone 中的集合擁有一個 url
屬性,定義了使用 Ajax GET 請求從伺服器取出 JSON 資料的位置,如
清單 14 所示。
清單 14. 集合的 url
屬性和 fetch()
方法
teams.url = '/getTeams'; teams.fetch(); //Ajax GET Request to '/getTeams'
Fetch()
方法屬於非同步呼叫,因此,在等待伺服器響應時,應用程式不會中止。在一些情況下,要操作來自伺服器的原始資料,可以使用集合的
parse()
方法。正如
清單 15 所示。
清單 15. parse()
方法
App.Collections.Teams = Backbone.Collection.extend({ model : App.Models.Team, parse : function(data) { // 'data' contains the raw JSON object console.log(data); } });
集合提供的另一個有趣的方法是 reset()
,它允許將多個模型設定到一個集合中。reset()
方法可以非常方便地將資料引導到集合中,比如頁面載入,來避免使用者等待非同步呼叫返回。
檢視和客戶端模板
Backbone 中的檢視與典型 MVC 方法的檢視不一樣。Backbone 檢視可以擴充套件 Backbone.View
函式並顯示模型中儲存的資料。一個檢視提供一個由
el
屬性定義的 HTML 元素。該屬性可以是由 tagName
、className
和
id
屬性相組合而構成的,或者是通過其本身的 el
值形成的。清單 16 顯示了使用這不同方法組合
el
屬性的兩個不同檢視。
清單 16. Backbone 檢視樣例
// In the following view, el value is 'UL.team-element' App.Views.Teams = Backbone.View.extend({ el : 'UL.team-list' }); // In the following view, el value is 'div.team-element' App.Views.Team = Backbone.View.extend({ className : '.team-element', tagName : 'div' });
如果 el
、tagName
、className
和 id
屬性為空,那麼會預設將一個空的 DIV 分配給
el
。
如上所述,一個檢視必須與一個模型相關聯。該模型屬性也很有用,如
清單 17 所示。App.View.Team
檢視被繫結到一個 App.Models.Team
模型例項。
清單 17. Backbone 檢視中的模型屬性
// In the following view, el value is 'UL.team-element' App.Views.Team = Backbone.View.extend({ ... model : new App.Models.Team });
要渲染資料(這是檢視的主要目的),重寫 render()
方法和邏輯來顯示 DOM 元素(由 el
屬性引用的)中的模型屬性。清單 18 展示了一個 render 方法如何更新使用者介面的樣例。
清單 18. Render()
方法
App.Views.Team = Backbone.View.extend({ className : '.team-element', tagName : 'div', model : new App.Models.Team render : function() { // Render the 'name' attribute of the model associated // inside the DOM element referred by 'el' $(this.el).html("<span>" + this.model.get("name") + "</span>"); } });
Backbone 也可以促進客戶端模板的使用,這就使得我們沒有必要在 JavaScript 中嵌入 HTML 程式碼,如 清單 18 所示。(使用模板,模板會封裝檢視中常見函式;只指定此函式一次即可。)Backbone 在 underscore.js(一個必須的庫)中提供一個模板引擎,儘管沒有必要使用該模板引擎。清單 19 中的例項使用 underscore.js HTML 模板。
清單 19. HTML 含有模板
<script id="teamTemplate" type="text/template"> <%= name %> </script>
清單 20 顯示了另一個使用 underscore.js HTML 模板的樣例。
清單 20. 使用 _.template()
函式的檢視
App.Views.Team = Backbone.View.extend({ className : '.team-element', tagName : 'div', model : new App.Models.Team render : function() { // Compile the template var compiledTemplate = _.template($('#teamTemplate').html()); // Model attributes loaded into the template. Template is // appended to the DOM element referred by the el attribute $(this.el).html(compiledTemplate(this.model.toJSON())); } });
Backbone 中最有用且最有趣的一個功能是將 render()
方法繫結到模型的變更事件中,如
清單 21 所示。
清單 21. Render()
方法繫結到模型變更事件
// In the following view, el value is 'div.team-element' App.Views.Team = Backbone.View.extend({ model : new App.Models.Team, initialize : function() { this.model.bind("change", this.render, this); } })
上述程式碼將 render()
方法繫結到一個模型的變更事件中。當模型發生更改時,會自動觸發 render()
方法,從而節省數行程式碼。從 Backbone 0.5.2 開始,bind()
方法就開始接受使用第三個引數來定義回撥函式的物件。(在上述示例中,當前檢視是回撥函式
render()
中的物件)。在 Backbone 0.5.2 之前的版本中,必須使用 underscore.js 中的 bindAll
函式,如
清單 22 所示。
清單 22. _.bindAll()
usage
// In the following view, el value is 'div.team-element' App.Views.Team = Backbone.View.extend({ initialize : function() { _.bindAll(this, "render"); this.model.bind("change", this.render); } })
Backbone 檢視中,通過檢視中的 DOM 物件監聽事件是比較容易的。對於實現這一點,events
屬性很是方便的,如
清單 23 所示。
清單 23. 事件屬性
App.Views.Team = Backbone.View.extend({ className : '.team-element', tagName : 'div', events : { "click a.more" : "moreInfo" }, moreInfo : function(e){ // Logic here } })
events
屬性的每個項均由兩部分構成:
- 左邊部分指定事件型別和觸發事件的選擇器。
- 右邊部分定義了事件處理函式。
在
清單 23 中,當使用者通過 DIV 中的類 more
以及類 team-element
點選連結時,會呼叫函式
moreInfo
。
結束語
MVC 模式可以為大型 JavaScript 應用程式提供所需的組織化程式碼。Backbone 是一個 JavaScript MVC 框架,它屬於輕量級框架,且易於學習掌握。模型、檢視、集合和路由器從不同的層面劃分了應用程式,並負責處理幾種特定事件。處理 Ajax 應用程式或者 SPI 應用程式時,Backbone 可能是最好的解決方案。
轉載: http://www.ibm.com/developerworks/cn/web/wa-backbonejs/
相關文章
- 開始學習啦
- 開始學習OpenRestyREST
- 今天開始學習 goGo
- 開始學習Python!Python
- 開始重新學習JAVAJava
- 開始學習Mysql了MySql
- 今天開始學習PYTHONPython
- 從頭開始學習VuexVue
- 從零開始學習KafkaKafka
- 被迫開始學習Typescript —— classTypeScript
- 從零開始學習機器學習機器學習
- 開始學習 PixiJSJS
- 開始學習電商實戰
- 從零開始學習laravelLaravel
- 被迫開始學習Typescript —— interfaceTypeScript
- go學習--->開始編碼Go
- Java Web學習之旅開始JavaWeb
- 開始學習Functional ProgrammingFunction
- SQL Server開始學習感受SQLServer
- 我想開始學習javaJava
- 開始我的學習之旅。。。。
- 開始學習除錯MySQL除錯MySql
- 從0開始學習java,應該從那塊開始學起?Java
- 如何開始學習Go語言Go
- 從0開始學習Webpack(一)Web
- 學習小程式從“你”開始
- 開始學習使用Node.jsNode.js
- 明天開始系統學習oracleOracle
- 如何開始學習 Node.js?Node.js
- eclipse學習從零開始Eclipse
- 從頭開始學習vue-routerVue
- 從零開始學習 Go ——安裝Go
- 學習React,從攻克JSX開始ReactJS
- 從這裡開始學習 LinuxLinux
- 30天從零開始學習SwiftSwift
- 從零開始學習C++(0)C++
- 從零開始學習OpenGL-14複習光照
- 《谷歌JAX深度學習從零開始學》簡介谷歌深度學習