## Backbone.View(視圖)
Backbone 視圖幾乎約定比他們的代碼多 — 他們并不限定你的HTML或CSS, 并可以配合使用任何JavaScript模板庫。 一般是組織您的接口轉換成邏輯視圖, 通過模型的支持, 模型變化時, 每一個都可以獨立地進行更新, 而不必重新繪制該頁面。我們再也不必鉆進 JSON 對象中,查找 DOM 元素,手動更新 HTML 了,通過綁定視圖的 `render` 函數到模型的 `"change"` 事件 — 模型數據會即時的顯示在 UI 中。
**extend**`Backbone.View.extend(properties, [classProperties])`
開始創建自定義的視圖類。 通常我們需要重載 [render](#View-render) 函數,聲明 [events](#View-delegateEvents), 以及通過 `tagName`, `className`, 或 `id` 為視圖指定根元素。
```
var DocumentRow = Backbone.View.extend({
tagName: "li",
className: "document-row",
events: {
"click .icon": "open",
"click .button.edit": "openEditDialog",
"click .button.delete": "destroy"
},
initialize: function() {
this.listenTo(this.model, "change", this.render);
},
render: function() {
...
}
});
```
直到運行時, 像`tagName`, `id`, `className`, `el`, 和 `events`這樣的屬性也可以被定義為一個函數,
**constructor / initialize**`new View([options])`
有幾個特殊的選項, 如果傳入,則直接注冊到視圖中去: `model`, `collection`, `el`, `id`, `className`, `tagName`, `attributes` 和 `events`。 如果視圖定義了一個**initialize**初始化函數, 首先創建視圖時,它會立刻被調用。 如果希望創建一個指向 DOM 中已存在的元素的視圖,傳入該元素作為選項:`new View({el: existingElement})`。
```
var doc = documents.first();
new DocumentRow({
model: doc,
id: "document-row-" + doc.id
});
```
**el**`view.el`
所有的視圖都擁有一個 DOM 元素(**el** 屬性),即使該元素仍未插入頁面中去。 視圖可以在任何時候渲染,然后一次性插入 DOM 中去,這樣能盡量減少 reflows 和 repaints 從而獲得高性能的 UI 渲染。 `this.el` 可以從視圖的 `tagName`, `className`, `id` 和 `attributes` 創建,如果都未指定,**el** 會是一個空 `div`。
```
var ItemView = Backbone.View.extend({
tagName: 'li'
});
var BodyView = Backbone.View.extend({
el: 'body'
});
var item = new ItemView();
var body = new BodyView();
alert(item.el + ' ' + body.el);
```
**$el**`view.$el`
一個視圖元素的緩存jQuery對象。 一個簡單的引用,而不是重新包裝的DOM元素。
```
view.$el.show();
listView.$el.append(itemView.el);
```
**setElement**`view.setElement(element)`
如果你想應用一個Backbone視圖到不同的DOM元素, 使用**setElement**, 這也將創造緩存`$el`引用,視圖的委托事件從舊元素移動到新元素上。
**attributes**`view.attributes`
屬性的鍵值對, 將被設置為視圖`el`上的HTML DOM元素的屬性, 或者是返回這樣的鍵值對的一個函數。
**$ (jQuery)**`view.$(selector)`
如果頁面中引入了 jQuery,每個視圖都將擁有 **$** 函數,可以在視圖元素查詢作用域內運行。 如果使用該作用域內的 jQuery 函數,就不需要從列表中指定的元素獲取模型的 ids 這種查詢了,我們可以更多的依賴 HTML class 屬性。 它等價于運行:`view.$el.find(selector)`。
```
ui.Chapter = Backbone.View.extend({
serialize : function() {
return {
title: this.$(".title").text(),
start: this.$(".start-page").text(),
end: this.$(".end-page").text()
};
}
});
```
**template**`view.template([data])`
雖然模板化的視圖 不是Backbone直接提供的一個功能, 它往往是一個在你視圖定義**template**函數很好的約定。 如此, 渲染你的視圖時, 您方便地訪問實例數據。 例如,使用Underscore的模板:
```
var LibraryView = Backbone.View.extend({
template: _.template(...)
});
```
**render**`view.render()`
**render** 默認實現是沒有操作的。 重載本函數可以實現從模型數據渲染視圖模板,并可用新的 HTML 更新 `this.el`。 推薦的做法是在 **render** 函數的末尾 `return this` 以開啟鏈式調用。
```
var Bookmark = Backbone.View.extend({
template: _.template(...),
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
});
```
Backbone并不知道您首選HTML模板的方法。 **render**(渲染) 函數中可以采用拼接HTML字符串,, 或者使用`document.createElement`生成DOM樹。 但還是建議選擇一個好的 Javascript 模板引擎。 [Mustache.js](http://github.com/janl/mustache.js), [Haml-js](http://github.com/creationix/haml-js), 和 [Eco](http://github.com/sstephenson/eco) 都是很好的選擇。 因為[Underscore.js](http://www.css88.com/doc/underscore/)已經引入頁面了,如果你喜歡簡單的插入JavaScript的樣式模板。 ?[_.template](http://www.css88.com/doc/underscore/#template)可以使用并是一個很好的選擇。
無論基于什么考慮,都_永遠_不要在 Javascript 中拼接 HTML 字符串。 在DocumentCloud中, 我們使用[Jammit](http://documentcloud.github.com/jammit/) 來打包JavaScript模板,并存儲在`/app/views`中,作為我們主要的`core.js`包的一部分。
**remove**`view.remove()`
從 DOM 中移除一個視圖。同事調用[stopListening](#Events-stopListening)來移除通過 [listenTo](#Events-listenTo)綁定在視圖上的 所有事件。
**delegateEvents**`delegateEvents([events])`
采用 jQuery 的`on`函數來為視圖內的 DOM 事件提供回調函數聲明。 如果未傳入 **events** 對象,使用 `this.events` 作為事件源。 事件對象的書寫格式為 `{"event selector": "callback"}`。 省略 `selector` 則事件被綁定到視圖的根元素(`this.el`)。 默認情況下,`delegateEvents` 會在視圖的構造函數內被調用,因此如果有 `events` 對象,所有的 DOM 事件已經被連接, 并且我們永遠不需要去手動調用本函數。
`events` 屬性也可以被定義成返回 **events** 對象的函數,這樣讓我們定義事件,以及實現事件的繼承變得更加方便。
視圖 [render](#View-render) 期間使用 **delegateEvents** 相比用 jQuery 向子元素綁定事件有更多優點。 所有注冊的函數在傳遞給 jQuery 之前已被綁定到視圖上,因此當回調函數執行時, `this` 仍將指向視圖對象。 當 **delegateEvents** 再次運行,此時或許需要一個不同的 `events` 對象,所以所有回調函數將被移除,然后重新委托 — 這對模型不同行為也不同的視圖挺有用處。
搜索結果頁面顯示文檔的視圖看起來類似這樣:
```
var DocumentView = Backbone.View.extend({
events: {
"dblclick" : "open",
"click .icon.doc" : "select",
"contextmenu .icon.doc" : "showMenu",
"click .show_notes" : "toggleNotes",
"click .title .lock" : "editAccessLevel",
"mouseover .title .date" : "showTooltip"
},
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
},
open: function() {
window.open(this.model.get("viewer_url"));
},
select: function() {
this.model.set({selected: true});
},
...
});
```
**undelegateEvents**`undelegateEvents()`
刪除視圖所有委托事件。如果要從臨時的DOM中禁用或刪除視圖時,比較有用。