MVVM和viewModel
Knockout是建立在以下三大核心功能之上的:
- 監控屬性和依賴跟蹤(Observables and dependency tracking)
- 聲明式綁定(Declarative bindings)
- 模板(Templating)
首先讓我們先來了解下MVVM模式,和viewModel的概念。
MVVM(model、view、viewModel)是一種創建用戶界面的設計模式,他旨在將一個復雜的UI簡單的分割為三個部分:
- model : 在你的應用用作存儲數據;
- view : 一個可見的、交互的UI來展現viewModel的狀態;
- viewModel : 用純凈的代碼來展現ui的數據和操作,可是任意的javascript對象
首先創建一個viewModel,僅僅需要聲明一個任意的javascript對象,例如:
var viewModel = {
name : 'knockout' };
然后為viewModel創建一個聲明式的綁定view,例如:
<div id="myview" data-bind="text : name"></div>
最后激活knockout,例如
ko.applyBindings(viewModel,document.getElementById('myview'));
data-bind屬性畢竟不是原生的html屬性,即使它能夠完美的工作,但是瀏覽器不認識它,你需要激活knockout使它生效。
這樣就把一個最簡單的viewModel和view綁定到了一起,能夠在view里顯示viewModel的屬性name了,但是,view卻不能再viewModel改變的時候自動更新,viewModel也不能再view改變是自動更新!
Observables
knockout的一個重要功能observable(監控屬性)就是能夠自動自動探測相關的依賴與通知訂閱者(automatic dependency detection and notification triggering),即在建立綁定關系的view和
viewModel之間能夠在一方改變的時候更新另一方的值。
我們將viewModel稍作修改:
var viewModel = {
name : ko.observable("knockout") };
現在他就能監控到變化,view就能夠自動更新了。
監控屬性的讀寫操作:
1.讀取監控屬性的值:
viewModel.name();
2.寫一個新值到監控屬性中:
viewModel.name("new value");
Observable Arrays
如果你想探測並響應一個對象的變化,使用observables,但是,如果你想探測並響應一系列集合的變化,你應該使用observableArray。
當你需要重復的增加或者刪除時需要顯示或者隱藏一些UI的區域時,observableArray能夠派上大用場。
var myObservableArray = ko.observableArray(); // Initially an empty array myObservableArray.push('Some value'); // Adds the value and notifies observers
注:observableArray跟蹤的是數組中的對象,而不是對象的狀態。單純的push一個對象到observableArray中,這個對象的屬性並不是observable的,observableArray僅僅跟蹤它所擁有的對象,並當有對象從數組中添加或刪除時通知監聽者。
也可以像這樣初始化一個observableArray:
// This observable array initially contains three objects var anotherObservableArray = ko.observableArray([ { name: "Bungle", type: "Bear" }, { name: "George", type: "Hippo" }, { name: "Zippy", type: "Unknown" } ]);
Computed Observables (依賴監控屬性)
如果你有一個observable的firstname和一個observable的lastname,你想顯示fullname怎么辦?computed observables派上用場了,它是一個依賴於其他一個或者多個observables的函數,並在任意一個依賴的observable更新時它能夠自動更新。
例如:有這樣一個viewModel:
function AppViewModel() { this.firstName = ko.observable('Bob'); this.lastName = ko.observable('Smith'); }
你可以添加一個computed observable來得到fullname:
function AppViewModel() { // ... leave firstName and lastName unchanged ... this.fullName = ko.computed(function() { return this.firstName() + " " + this.lastName(); }, this); }
綁定UI元素:
The name is <span data-bind="text: fullName"></span>
當firstname或者lastname改變時fullname能夠自動更新。
你可以這樣使用:
- 監控屬性items表示items的集合;
- 監控屬性selectedIndexes存儲當前選中的items的index集合;
- 依賴監控屬性selectedItems表示當前選中的items集合;
改變items或者selectedIndexes都能夠使依賴監控屬性(selectedItems)更新,以達到更新相應的UI元素的目的。
管理this :
ko.computed的第二個參數決定了this的值當我們計算依賴監控屬性的值的時候,如果不傳它,將不能夠引用到this.firstname()或者this.lastname().
你也可以這樣做,更加方便:
function AppViewModel() { var self = this; self.firstName = ko.observable('Bob'); self.lastName = ko.observable('Smith'); self.fullName = ko.computed(function() { return self.firstName() + " " + self.lastName(); }); }
self能夠在funciton的閉包中被捕獲到,在它任何的內嵌的function中任然是可用的。
判斷一個屬性是否為依賴監控屬性(computed observable)
for (var prop in myObject) { if (myObject.hasOwnProperty(prop) && !ko.isComputed(myObject[prop])) { result[prop] = myObject[prop]; } }
另外,ko提供了一些函數能夠操作監控屬性和依賴監控屬性:
- ko.isObservable : 對於所有的observable,observable array,computed observable將返回true;
- ko.isWritableObservable : 對於所有的observable,observable array,writable computed observable返回true;
使用peek控制依賴關系:
正常情況下,ko的自動依賴跟蹤能夠完成你所期望的工作,但是,你也許希望控制哪個observable來更新或者不更新你的computed observable,peek()函數能夠使你的observable或者computed不創建依賴關系。
例如:
ko.computed(function() { var params = { page: this.pageIndex(), selected: this.selectedItem.peek() }; $.getJSON('/Some/Json/Service', params, this.currentPageData); }, this);
在這個例子中,一個computed observable依賴其他兩個observable使用ajax獲取數據,當pageIndex改變的時候,computed observable就會更新,重新獲取數據,但是computed observable會忽略掉selectedItem的改變,
因為它使用了peek()。在這種情況下,用戶也許只希望使用selectedItem的值僅僅作為跟蹤目的當一個新的數據集合下載完成以后。
構造computed observable
一個computed observable可以使用以下方式構造:
1.ko.computed( evaluator [, targetObject, options] )
-
- evaluator : 一個用來計算當前依賴監控屬性的值的函數。
- targetObject : 可選,如果定義了,它將作為ko激活你的回調函數時候的this值。
- options : 可選,備用。
2.ko.computed(options),options包含以下屬性:
-
- read : 必須的。用來計算當前依賴監控屬性的值的函數。
- write : 可選的。使依賴監控屬性writable,這個函數接收一個value參數以用來write到computed observable中。
- owner : 可選的。如果定義了,它將作為回調函數的this值。
- pure : 可選的。如果為true,這個computed observable將被設置為pureComputed observable。
- deferEvaluation : 可選的。不常用
- disposeWhen : 可選的。不常用
- disposeWhenNodeIsRemoved : 可選的。不常用。
computed observable的使用
- dispose() : 手動管理依賴監控屬性,清楚所有依賴的訂閱關系,這個函數非常有用,當你想停止computed observable的更新或者清除內存的時候
- extend(exteners) : 擴展方法。
- getDependenciesCount() : 返回當前computed observable的依賴數量。
- getSubScriptionsCount() :
- isActive() : 返回computed observable是否會更新在將來的某個時候,一個沒有任何依賴的computed observable是不會被激活的
- peek() : 返回computed observable中沒有創建依賴關系的值。
- subscribe(callback,[callbackTarget,event]) : 注冊一個手動的訂閱來接收computed observable改變的通知。