談談FLUX的使用

提要:本文所展現示例代碼,既非JQuery代碼,也是不React代碼,爲介於二者間的,方便行文僞代碼。代碼意思經過上下文便可理解。javascript

Facebook的Jing Chen同窗在2014年的F8上演示了她的FLUX架構。前端

clipboard.png

FLUX的工做流以下:java

  1. 中央Dispatcher管理全部進來的ACTION。
  2. 能夠註冊任意多個Store處理ACTION.
  3. 組件在須要修改數據的時候,發出ACTION.

可是,若是隻是上述那個簡單的話,就實在沒什麼好說的了。舉個例子,若是我有一個TodoList,在點擊添加item的時候,須要把內容發送到服務器,根據服務器返回的結果,顯示成功或失敗的結果,那麼採用FLUX則是這麼寫的:ajax

javascript
dispatcher.register(function(payload){ switch(payload.actionType) { case "ADD_ITEM": ajax("/add/item", payload.data, function success(res){ dispatcher.dispatch("ADD_SUCCESS", payload.data); }, function failure(){ dispatcher.dispatch("ADD_FAIL", payload.data); }); break; case "ADD_SUCESS": // do something break; case "ADD_FAIL" // do something break; } }); // 在組件中發出 $("#add").click(function(){ dispatcher.dispatch({ actionType: "ADD_ITEM", data: $("input").val() }); });

上述代碼是有問題的,由於在Store中發出異步請求,而後在回調中又發出ACTION,這種狀況屬於Nested Update,致使的問題有:服務器

  • 你不知道你發出的action,何時纔會處理完
  • 你不知道你的事件,數據的流向在哪兒
  • 容易引起循環調用

固然,最最終致使的結果就是:難以維護。架構

所以,在FLUX中,首要的一個原則就是:異步

STORE代碼同步化函數

上面的代碼能夠改爲:測試

dispatcher.register(function(payload){
    switch(payload.actionType) {
        case "ADD_ITEM":
            // 顯示小菊花,表示"正在加載"
            break;
        case "ADD_SUCESS":
            // 顯示加載成功
            break;
        case "ADD_FAIL"
            // 顯示加載失敗
            break;
    }
});

$("#add").click(function(){
    var msg = $("input").val();
    dispatcher.dispatch({
        actionType:"ADD_ITEM", 
        data: msg
    });

    ajax('add/item', msg, function success(res) {
        dispatcher.dispatch({
            actionType:"ADD_SUCESS",
            data: res
        });
    }, fail(res){
        dispatcher.dispatch({
            actionType:"ADD_FAIL"
            data: res
        });
    });

});

這樣在store中的三個case裏的操做都是同步的了,即任何action,咱們都很清楚其觸發了以後,具體發生了什麼事情。this

注:不要被上面代碼中的註釋迷惑,Store並不該該去操做DOM,上面的註釋只是說明其以後發生了什麼。

上面的例子中,咱們將ajax請求放在組件中,成功時觸發一個action,失敗時又觸發另外一個,使得程序行爲是徹底可預測的。但若是這個ajax請求的數據很是複雜呢,組件內部就沒法發出請求了,由於它沒有收集這些數據的能力。

舉個例子,在一個購物車頁面內,勾選增長/刪除某個商品,會:

  • 商品列表改變了
  • 收集全部其它已勾選的商品及數量
  • 發送ajax到後臺,計算總價(不要問我爲何,這裏涉及大量的滿減,積分,立減等碧池邏輯)
  • 後臺返回計算後的總價
  • 更新前端

上面的關鍵步驟發送ajax到後臺,咱們並不能在商品數量組件中發送,由於單個組件沒法收集整個商品列表的信息,若是它能夠收集的話,你的程序就很差維護了,由於你的組件強依賴於外部信息。

這種狀況彷佛又繞回了篇首的寫法,由於store中有相應的信息,那麼就在store收集數據,發出ajax呀。(又有同窗提議說,讓組件從Store中取數據,不就能收集到所需數據了嗎?)。 注意,FLUX另外一原則是:

不要讓你的組件強依賴於Store,你組件的數據應該來自於父組件,而不是Store。強依賴於Store的後果是:

* 你的組件難以複用
* 你的組件難以測試

在此例中,我能想到的解法應該是,經過父組件傳入回調給子組件處理:

// 購物車中
<Cart>
    <Quantity onChange={function(quantity){
        // 1. 收集全部商品信息
        var info = getAllDealsInfo();
        // 2. 當前商品數量+1
        info[this.id]++;

        // 3. 發出請求
        ajax('/checkTotal', info, function(total) {
            // 當服務器返回時,
            dispatcher.disptach({
                actionType: 'CHANGE_TOTAL',
                total: total
            });
        });
    }} />

</Cart>


//數量組件中
input.on('change', function(){
    // 發出action,可能某個角落有store監聽這個action
    // 用以統計當前全部商品的數量
    dispatch.dispatch({
        actionType: "CHANGE_QUANTITY",
        id: this.id,
        quantity: this.value
    });
    // 調用父組件傳來的onChange函數
    this.props.onChange();
});

如此,一可保持Store中代碼的同步化,二可保持組件中對外部世界的無知。

FLUX另外最重要的特色就是,很是容易擴展。舉例而言,在上面購物車例子中,若是我要增長一個區塊顯示每樣商品的數量,我不須要修改原有的代碼,只須要新增長一個store便可。

dispatcher.register(function(payload) {
     switch(payload.actionType){
         case "CHANGE_QUANTITY":
             // 1. 收集全部商品的數量
             // 2. update對應的model,更新view
             break;
     }
});

【完】

相關文章
相關標籤/搜索