WeX5的正確打開方式(3)——綁定機制

今天整理一下WeX5的綁定機制。
原生的問題
假設咱們作一個訂單系統,須要顯示商品單價,而後能夠根據輸入數量計算出總價並顯示出來。使用原生代碼也很容易實現,效果:
         圖片描述
代碼以下:html

<!--HTML code-->
Price: <span id="price"></span><br />
Account: <input type="text" id="account" value="" placeholder="請輸入數量" /><br />
sum: <span id="sum"></span>
//js codevar priceNode = document.getElementById('price'),
    accountNode = document.getElementById('account'),
    sumNode = document.getElementById('sum'),
    price = 100,
    account = 11,
    sum = price * account;//初始化。
priceNode.innerText = price;
accountNode.value = account;
sumNode.textContent = sum;

//監視 View層的用戶輸入
accountNode.addEventListener('keydown', function (e) {
    window.setTimeout(function () {
        account = accountNode.value;
        sum = price * account;
        sumNode.textContent = sum;
    },10);
});

嗯,蠻簡單的!哦,對了,咱們一次展現50件商品,同時又有10類這樣的展現,還有買5盒岡本送一根油條這樣的各類促銷呢……
因此,你知道原生實現的問題了吧:jquery

  • 隨着 UI 和數據交互的增多,代碼量迅速增加,難以維護json

  • 基於 Dom 查詢,id 或 class 的命名難以管理數組

  • 代碼耦合度高,難以複用app

WeX5的解決之道
    爲了解決上述問題,WeX5中引入了knockoutjs(下文簡稱ko)這個MVVM庫。
爲什麼選用ko而不是Angular一類比較全面的框架?Angular是好,但這麼大而全的框架,沒有通過足夠的實戰測試的話,不少坑都不會被暴露出來。而ko是一個輕量級的MVVM庫,專一於實現數據與視圖的綁定,自己並不提供 UI 類和路由等功能,因此很是簡單穩定。同時,因爲他出來也已經有些年頭了,如今是比較成熟的框架了。因此在作一些移動頁面開發時,ko無疑是一個比較好的選擇。另外,關於MVVM小茄就很少說了,一圖以蔽之:框架

圖片描述

    ko創建在3大核心特徵之上(官網介紹):編輯器

  1. 可觀察對象與依賴跟蹤 (Observables and dependency tracking):使用可觀察對象在模型數據之間設立隱性關係鏈,用於數據轉換和綁定。函數

  2. 聲明式綁定 (Declarative bindings):使用簡單易讀的語法方便地將模型數據與DOM元素綁定在一塊兒。工具

  3. 模板 (Templating):內置模板引擎、爲你的模型數據快速編寫複雜的 UI 展示。
    下面簡單說說ko的幾大概念:性能

可觀察對象
    使用ko重寫上面的例子(自訂價格,這也是我小時候的願望之一):
   
代碼是這樣的:

<!--HTML Code--><div id="one">
    Price: <input type="text" data-bind="value: price" placeholder="請輸入單價" /><br />
    Account: <input type="text" data-bind="value: account" placeholder="請輸入個數" /><br />
    sum: <span data-bind="text: sum"></span></div>
// js Codevar ViewModel = function(p, a) {
    //設置爲可觀察對象並以參數p、a初始化
    this.price = ko.observable(p);
    this.account = ko.observable(a);
    //調用ko函數的時候將this傳入,不然執行ko.pureComputed內部代碼時,this爲ko,ko.price()報錯。
    this.sum = ko.pureComputed(function() {
        //由於可觀察對象是一個函數對象,因此要用 price()來讀取當前值。
        //設置值使用price(NewValue),支持鏈式寫法:this.price(12).account(3)
        return this.price() * this.account();
    }, this);
};var vm = new ViewModel(135, 10);//應用該綁定,綁定開始生效
ko.applyBindings(vm);

1)先看HTML代碼:
能夠看到在每一個標籤中都加入了一個 data-bind = "XX:OO" 這樣的鍵-值對。這個就是 ko 的綁定語法,XXOO表明什麼東西呢?(XXOO?小茄仍是個孩子啊…)從例子能夠看到XX爲標籤的屬性,能夠是text、value、class、checked等標籤屬性,其實也能夠是click、focus、load等DOM事件。OO看起來像是一個變量,實際上並非變量,而是一個函數對象,執行這個函數(帶個())就能獲得相應的綁定值。經過XXOO就能夠將元素的屬性或事件跟js中的函數對象綁定在一塊兒(XXOO過了就要相互負責?),這就是ko的聲明式綁定。綁定的定義其實就是一個觀察者模式,只不過這是雙向的綁定,發佈者和訂閱者相互訂閱了對方的消息而已,這就是MVVM的雙向綁定。ko雙向綁定的結果就是一方變化就能夠自動更新另外一方,也就是經過ViewModel將數據和表現層牢牢綁定在一塊兒了。綁定的效果相似於:
圖片描述
2)再看看js代碼:
能夠看到js中定義了一個ViewModel對象,在對象中對HTML中綁定的OO進行了操做。這裏主要有兩個操做: ko.observable()和ko.pureComputed()。

  • ko.observable(p):見名知義、這個就是設置可觀察對象的方法,傳入的參數p就是初始化的值,這裏的參數能夠是基本數據類型,也能夠是一個json對象。被設置爲可觀察對象後就意味着系統會一直觀察這個值。不管是ViewModel中的p仍是被綁定對象的p發生變化都會引發刷新事件,將全部用到這個值的地方都更新到最新狀態。顯然,可觀察對象是比較消耗性能的,因此對於不須要動態變動的值(如價格)則不要設置爲可觀察對象,固然仍是須要放入ViewModel中進行集中初始化。

  • 注意:ko.observable(p)返回的可觀察對象是一個函數對象,因此讀取可觀察對象須要使用price()這種方式;一樣的,設置可觀察對象須要使用price(newValue)這種方式。比較貼心的是,設置的時候支持鏈式寫法:ViewModel.price(100).account(10)。

  • ko.pureComputed()就是所謂的依賴跟蹤了,這裏是單價*數量等於總價,注意這裏不能直接用this.sum =
    this.price() * this.account();來指定sum,這種寫法不能動態刷新被綁定的對象,只是動態改變了sum變量,但要去刷新綁定對象還須要其餘操做。因此,與計算相關的綁定值都要用ko的計算函數來設置。固然,返回的也是一個函數對象。另外,ko還有一個computed函數,也能夠用其來進行設置,不過推薦使用pure的方式,以提升性能。

  • 注意這裏的寫法:ko.pureComputed(fn,
    this),也就是將fn綁定到ViewModel執行環境中,其實就是js中的call/apply。由於在執行ko內部函數的時候,this爲ko對象,因此爲了獲得ViewModel對象,須要經過上面的寫法傳入this。固然也能夠在ko函數外部用that保存ViewModel對象,而後在ko函數內部使用that來調用ViewModel對象。像這樣:

var that = this;this.sum = ko.pureComputed(function() {
    return that.price() * that.account();
});

    定義好ViewModel構造函數後便實例化了一個ViewModel對象,而後使用了ko.applyBindings()的方式來使得綁定生效,這一步不要漏掉了。
使用ko的頁面簡單模式:

<!--HTML Code--><span data-bind="text: bindtext"></span>
// js Codevar viewModel = {
    bindtext: ko.observable('initValue')
};
ko.applyBindings(viewModel);

    總結起來就是:HTML中使用data-bind="XX: OO"聲明綁定,js中創建ViewModel並設置可觀察對象,最後應用綁定。
可觀察對象數組
    再看看可觀察對象數組的使用方法,在ko中可不能像js同樣數組和變量混用,對於數組對象就要用ko.observableArray([…,…])這種形式,一樣的,數組元素也能夠是基本類型也能夠是json對象。ko中的可觀察對象數組有一系列的數組操做方法,如slice()、sort()、push()這種,效果跟原生的js數組操做方法同樣,只是經過ko方法所作的改動會通知到訂閱者從而刷新界面,但js方法則不會刷新界面。下面是一個簡單例子:
圖片描述

<!--HTML Code--><select data-bind="options: list"></select>
// js Codevar vm = {
    // list: ko.observableArray()
    list: ko.observableArray(['Luffy','Zoro','Sanji'])
};
ko.applyBindings(vm);

    關鍵點:ko監控的是數組的狀態,而不是元素自己的狀態。也就是說當數組狀態變化(增減元素)的時候會觸發ko事件引發綁定對象的刷新,但數組內部元素的變化(如:值變化)則不被監控不能觸發ko事件。例如:
圖片描述
    在控制檯中使用原生方法將Luffy動態改爲Lucy是不會刷新UI頁面的,而使用ko的數組操做改動數組則會當即刷新頁面,值得注意的是在刷新的時候,也會將以前的改動刷新出來(Luffy > Lucy)。也就是說其實js內存中的變量是已經改變了,可是還缺乏一個刷新DOM的動做。這裏你們能夠看到,讀取數組的方法是vm.list()[0],由於list也是一個函數對象,執行返回值纔是咱們想要的list內容。同理,也能夠經過 vm.list(["妹子","妹子","妹子"]) 這樣的方式重置可觀察對象數組,也能當即刷新UI。
若是須要將數組元素的改動也動態反應到UI上,須要將數組元素也設置爲可觀察對象,而後使用ko的方法改變數組元素值。注意,是使用ko的方法 list()0
圖片描述
    操做可觀察對象數組的方法有兩類,一類是與原生js數組方法同名的:pop, push, shift, unshift, reverse, sort, splice,這一部分與js原生方法的用法和效果都同樣,就再也不贅述了。
另一些方法是js中沒有的,主要有如下幾個:

  • remove(someItem) --
    刪除全部值與someItem相等的元素項並將它們以數組形式返回,這裏的意思就是說你可不能直接list.remove(0)來刪除第一項,而是要用list.remove(list()[0])

這種形式來刪除。總而言之,傳入的參數必須是元素項的值,能夠用list()[0]
的形式,也能夠直接輸入值的字符串(好比「Luffy」這種)。

  • remove(function(item) { return item.age < 18;}) --
    刪除全部age屬性小於18的元素項並將它們以數組形式返回,這種用法跟日常的數組高階函數沒什麼區別。Item做爲高階函數的參數傳入,遍歷數組時,當高階函數返回值爲真值時就刪除該項,不然轉到下一項。

  • removeAll(['Chad', 132, undefined]) -- 刪除全部值與 'Chad' 或 123 或
    undefined 相等的元素項並將它們以數組形式返回。

  • removeAll() -- 刪除全部項並以數組形式返回。

小竅門:在處理可觀察對象時,若對象數量衆多並且交互頻繁的狀況下,每次變動都當即刷新的話會很是消耗性能,這個時候可使用擴展 myObservableArray.extend({ rateLimit: 1000 }) 來設置延遲刷新。好比在不斷往可觀察對象數組中插入元素時,能夠設置一個週期時間1000ms,讓1000ms內的全部操做集中到一次刷新中去,避免頻繁操做 DOM 帶來的性能惡化。
WeX5中如何使用ko?
    WeX5做爲Html5 開發工具界的翹楚,少不了集成優秀的ko框架,使用的方法很是簡單:在可視化編輯器中指定組件的bind屬性,而後在js代碼中操做相應綁定值。
    先在可視化編輯器中指定:
圖片描述圖片描述
    這種方法在hello world篇也有簡單介紹,不熟悉的同窗能夠先去看看哈。經過可視化編輯器咱們就能夠綁定相應的屬性或者事件了,這裏咱們爲 bind-ref 綁定了一個字符串「hello world」,至於其餘的屬性和事件將在下一篇中介紹。綁定後咱們打開代碼編輯器,發現裏面並無出現1)那樣的綁定代碼。那綁定代碼寫到哪裏去了呢?請打開HTML源碼:
010
能夠看見代碼中出現了「bind-ref='Hello World’」這個跟上文說的data-bind是否是很類似呢?這裏WeX5將每一個組件能夠綁定的屬性都添加到可視化編輯器中,這樣就不用再去記某個組件能夠綁什麼屬性了,鼠標指哪就綁哪!固然綁定字符串意義不大, 咱們通常會綁定一個變量(其實是返回值爲所需變量的函數對象)。例如:
011
這裏綁定了text爲myText,這種形式的綁定爲直接綁定在model對象下的,因此能夠在js源碼中的Model下操做這個myText對象。

1 define(function(require){  2     var $ = require("jquery");  3     var justep = require("$UI/system/lib/justep");  4   5     var Model = function(){  6         this.callParent();  7         this.myText = justep.Bind.observable("bind!");  8     };  9     Model.prototype.button2Click = function(event){ 10         this.myText.set("changed"); 11     }; 12     return Model; 13 }); 14

  能夠看到ko組件已經被封裝到justep的Bind對象裏面去了,另外對可觀察對象的操做也跟ko中有點不一樣,這裏採用的是set/get分別來設置和獲取可觀察對象的值。其餘諸如compute等大部分方法的用法跟ko中一致。
總結
本篇主要簡單介紹了WeX5中數據綁定的由來和背後的優秀框架(knockoutjs),着重介紹了ko中最重要的概念:可觀察對象(數組),而後簡單示範瞭如何在WeX5中使用綁定機制以及 WeX5中的綁定與ko中的差別點。
關於可觀察對象的簡單介紹就到這裏了,下一篇將具體介紹各類綁定的用法!碼字不易,隨手點贊哈~

參考資料:

  1. ko官方教程:http://knockoutjs.com/documentation/introduction.html

  2. WeX5綁定教程:http://docs.wex5.com/data-bind-instro/

相關文章
相關標籤/搜索