Utilities
Knockout 提供了許多可以你開發中使用的工具,你可以在 ko.utils 命名空間中找到它們,我最喜歡的工具如下所示:
- extend: 這個方法將兩個對象合並在一起,調用這個方法之后,會將第二個對象的所有屬性,方法合並到第一個對象上。
- unwrapObservable: 這個方法獲取一個屬性作為參數,然后返回它的值。比如 Knockout 的 Observable 屬性,或者一個簡單的屬性。這個函數在你希望在運行時獲取對象的實際值得時候很有用。
- 所有的數組工具:Knockout 提供了許多對於數據操作的工具,允許你過濾,映射,或者刪除其中的項目。我經常在項目開始的時候,將這些方法附加到ko.observableArray.fn 上。
下面的代碼演示了使用方法。
// extend usage var a = { val: 1 }, b = { val: 2 }; ko.utils.extend(a, b); console.log(a.val); // console: 2 // unwrapObservable usage var c = ko.observable(99), d = 98; console.log(ko.utils.unwrapObservable(c)); // console: 99 console.log(ko.utils.unwrapObservable(d)); // console: 98 // array "map" utility function usage var arr = [100, 101]; var mapped = ko.utils.arrayMap(arr, function (item) { return item + 50; }) console.log(arr); // console: [ 150, 151 ]
Data-bind statements
我們一直關注 Knockout 的腳本庫,實際上,Knockout 被設計為可以簡單地將 JavaScript 對象綁定到 HTML 中。API 使用與 HTML5 兼容的 data-bind 語法。在我們前面的示例中,你可以看到可以簡單地將 HTML 元素的屬性綁定到 ViewModel 的屬性上。data-bind 語法允許使用逗號分隔的綁定定義,下面的示例演示了使用方式。
<span data-bind="text: myText"></span> <div data-bind="visible: isVisible, with: someProp"></div> <input data-bind="value: someVal, css: { 'error': !someVal.isValid(), 'success': someVal.isValid() }"/>
Applying bindings
applyBindings 是 Knockout 一切工作啟動的起點。大多數的示例都演示了將一個 ViewModel 作為參數的使用方式。但是你可以通過第二個參數來指定一個 DOM 對象,Knockout 將只會綁定到這個 DOM 節點及其子節點上。
多數的應用只有一個 ViewModel ,在調用 applyBinding 的時候,也僅僅傳遞一個 ViewModel 參數。但是,我創建過許多復雜的應用程序,在一個頁面中使用了多個 ViewModel 對象,使用多個 ViewModel 的分別處理提示,設置,還有當前用戶的信息等等。在這些情況下,通過限制 Knockout 綁定的節點數量,可以獲取性能的優勢。如果你僅僅需要更新頁面的部分內容,就不要通過 Knockout 綁定到整個頁面上。
Binding handlers
我曾經提到過 Knockout 提供了許多的擴展點。有一些比 Knockout 的 Binding handler 更方便。雖然通過 data-bind 語法實現了 binding handler 。Knockout 還允許我們自定義綁定的處理器,所以,我們可以實現,或者重寫,我們自定義的功能。
在 MVVM 風格的開發中, 我們會有兩種類型的綁定:單向和雙向綁定。單向的綁定是簡單的信息讀取,將 ViewModel 中的數據讀取出來綁定到 DOM 中。你可以想像出雙向的數據綁定就是在單向的基礎之上,將 DOM 對象的更新返回到 ViewModel 的屬性上。Knockout 允許我們創建所有類型的綁定,下面的代碼演示了基本處理器使用。
ko.bindingHandlers['myHandler'] = { init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { }, update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { } };
如你所見,我們提供了兩個鈎子來實現我們的邏輯。Init 和 update 函數。這些函數的參數如下所示:
- element: 定義 data-bind 的元素
- valueAccessor:返回 ViewModel 綁定屬性值得函數。如果綁定到 Observable 屬性,那么回返回這個 Observalbe,在我們的處理邏輯中,需要將這個 Observable 進行 upwarp處理。
- allBindingAccessor:類似於 valueAccessor ,但是它返回一個包含所有綁定和綁定值的對象。
- viewModel:傳遞給 applyBindings 的視圖模型或者根對象
- bindingContext:專用的一個特殊對象,包含表示當前綁定上下文環境的特定信息。綁定上下文中有一個 $data 屬性表示當前的綁定,這個屬性經常與 viewModel 是相同的。還有 $parent 屬性和 $parents 屬性,表示綁定元素的上級節點。通常在使用 with 的時候才會使用這些屬性。
你可以在想,這些東西看起來都差不多,我們應該在哪里實現我們的業務邏輯呢?init 函數僅僅在調用 applyBinding 的時候調用一次。Knockout 遍歷整個 DOM 樹,查找 data-bind 語法,處理它們,在每個需要的綁定上,調用 init 方法。然后,在調用 init 方法之后,立即調用 update 方法,以后,在綁定發生變化的時候會多次調用這個方法 ( 如果是 Subscribable )。
我的處理方式是在 init 中注冊我所有的事件處理器 ( change, blur, focus ) ,然后在 update 中處理 HTML。
下面的代碼演示了常見的單向綁定,這個例子與 visible 正好相反,我們可以使用 isHidden 而不是 isVisible。
// invisible -> the inverse of 'visible' ko.bindingHandlers['invisible'] = { update: function (element, valueAccessor) { var newValueAccessor = function () { // just return the opposite of the visible flag! return !ko.utils.unwrapObservable(valueAccessor()); }; return ko.bindingHandlers.visible.update(element, newValueAccessor); } };
下面的代碼演示了雙向的綁定,這個例子演示對一個數字的驗證,從元素中獲取數據傳遞到 ViewModel,在 ViewModel 發生變化的時候也可以傳遞到 DOM。
// simple number parsing function parseNumber(strVal){ return parseInt(strVal, 10); } // very basic two-way binding handler ko.bindingHandlers['number'] = { init: function (element, valueAccessor, allBindingsAccessor) { //handle the input changing ko.utils.registerEventHandler(element, "change", function () { var observable = valueAccessor(); var number = parseNumber(element.value); if (number !== NaN) { observable(element.value); } }); }, update: function (element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()); var number = parseNumber(value); if (number !== NaN) { element.setAttribute("value", number); } } };
Summary
你可以看到 Knockout 是一個非常成熟的腳本庫,在這一節我們學到下面的內容:
- Knockout 的核心對象和使用方式
- Knockout 的實用工具
- 如何創建和管理多個的 ViewModel
- 定制綁定處理器