谈谈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;
     }
});

【完】

相关文章
相关标签/搜索