Reflux应用架构学习分析

上篇中写了对flux的分析,这次来说说reflux吧。reflux是对flux的升级扩展,并且去掉了dispatcher这一层,比起使用起flux来,思路更加清晰,是一个标准的pub/sub模式的架构,同时能少写很多代码。这次我还是通过reflux的todoMvc来解释reflux的用法。

reflux介绍

reflux保留了flux中actionstore单向数据流这三个概念。我们主要来看下有哪些不同:

  • 移除了单例的dispatcher,用action来代替
  • store可以监听action的行为,无需进行冗余的switch判断
  • store可以相互监听,可以进行进一步的数据聚合操作,类似于map/reduce
  • waitFor被连续平行的数据流所替代

整个过程大致是这样的

1
2
3
4
5
6
7
╔═════════╗ pub ╔════════╗ pub ╔═════════════════╗
║ Actions ║────────> ║ Stores ║────────> ║ View Components
╚═════════╝<──────── ╚════════╝<──────── ╚═════════════════╝
^ sub sub │
|
└──────────────────────────────────────────┘
call

store监听action的行为,当页面上有操作触发action的时候,emit一个action事件,store监听到事件后执行store中的callback处理相应的逻辑,同时trigger出另外一个change事件,view通过监听store的变化,来执行setState,从而达到页面重新render更新。下面我们来具体看下todoMvc这个例子的具体实现吧。

Action的创建

首先是action的创建,可以通过createActions的方式,创建所有应用中可能用到的action。

1
2
3
4
5
6
7
8
global.TodoActions = Reflux.createActions([
"toggleItem", // called by button in TodoItem
"toggleAllItems", // called by button in TodoMain (even though you'd think TodoHeader)
"addItem", // called by hitting enter in field in TodoHeader
"removeItem", // called by button in TodoItem
"clearCompleted", // called by button in TodoFooter
"editItem" // called by finishing edit in TodoItem
]);

这里返回的是一个actions对象,里面包含了各个actionName及其对应的functor函数,而此时的functor已经继承了各种pub/sub的私有方法,方便后面使用。
如果说想创建单个的action,也可以使用createAction方法,这个是直接返回了一个functor

Store到Action的订阅

通过createStore方法来创建store,并且用onactionName 的方式添加各个action的回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
global.todoListStore = Reflux.createStore({
// this will set up listeners to all publishers in TodoActions, using onKeyname (or keyname) as callbacks
listenables: [TodoActions],
onEditItem: function(itemKey, newLabel) {
var foundItem = getItemByKey(this.list,itemKey);
if (!foundItem) {
return;
}
foundItem.label = newLabel;
this.updateList(this.list);
},
...
}

使用listenables: [TodoActions]来添加action和callback的绑定,推荐使用这种方式来做绑定。同时你也可以使用listenTo或者listenToMany方法对action做逐个绑定。在createStore内部存在一个Store构造函数,首先把所有回调函数赋值到构造函数的this对象上,通过listenToMany方法逐一对actionName做callbackName化(加 on 处理),然后在this对象上找到action所对应的回调函数,然后调用listenTo方法。传入到listenTo内部的listenable参数,其实就是我们前面提到的functor函数,在它上面已经绑定了listen这个私有方法。此时调用listen方法来添加addListener的监听,这样就完成了store到action的监听。

Action到Store的发布

在view组件中,通过鼠标事件或者是键盘操作触发action的执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
handleDestroy: function() {
TodoActions.removeItem(this.props.id);
},
render: function() {
var classes = React.addons.classSet({
'completed': this.props.isComplete,
'editing': this.state.isEditing
});
return (
<li className={classes}>
<div className="view">
<input className="toggle" type="checkbox" checked={!!this.props.isComplete} onChange={this.handleToggle} />
<label onDoubleClick={this.handleEditStart}>{this.props.label}</label>
<button className="destroy" onClick={this.handleDestroy}></button>
</div>
<input ref="editInput" className="edit" valueLink={this.linkState('editValue')} onKeyUp={this.handleValueChange} onBlur={this.handleBlur} />
</li>
);
}

我们通过click事件触发了一个action的删除操作。这里的TodoActions是前面提到的actions对象,TodoActions.removeItem()执行了functor函数。

1
2
3
4
var functor = function functor() {
var triggerType = functor.sync ? "trigger" : "triggerAsync";
return functor[triggerType].apply(functor, arguments);
};

最终在trigger函数内部实现了事件的emit,这样就完成了action到store的事件发布。每次操作action就会执行store中对应的回调函数。

Store到View的发布

上面介绍了action和store之间的关系,下面来说说store和view之间的关系。这两者之间也是种pub/sub模式。不知道你注意到没,在store中的每个回调最终都会调用updateList函数内部的this.trigger()方法。如注释写的,他是用来通知监听了store的组件,list被更新了。

1
2
3
4
5
6
7
// called whenever we change a list. normally this would mean a database API call
updateList: function(list){
localStorage.setItem(localStorageKey, JSON.stringify(list));
// if we used a real database, we would likely do the below in a callback
this.list = list;
this.trigger(list); // sends the updated list to all listening components (TodoApp)
}

这里this指向Store构造函数,trigger函数其实是emit了一个change事件。找到了发布者,我们再来看看订阅者。

View到Store的订阅

在view中通过mixins的方式,添加了view对store的监听。当有数据发生变化的时候,调用setState方法修改state的状态,重新渲染组件。

1
mixins: [Reflux.connect(todoListStore,"list")]

这样我们就完成了上图中所有的流程。

小结

与flux比起来,store与view之间的关系是一样的,都是通过controller-views来监听store的数据变化,当有数据变动后emit一个事件出来,view监听到事件后,修改state的状态来重新render更新页面。区别在于action与store之间个关系,flux通过dispatcher这个中间者来分发action和store之间的数据,reflux则是直接让action和store互相关联,省去了分发者这个角色。
对于flux来说,dispatcher也不是多余的存在,可以看下官网的解释。假设存在storeA和storeB,其中A又依赖了B,所以需要通过dispatcher提供的waitFor()方法来先执行storeB,dispatcher也确保了同一时间只能执行一个action,同时也规避了A依赖B,而B又依赖A这样循环依赖的存在。
篇幅有限,还有很多都没涉及到讲,可以看下下面参考链接里的内容。讲完了flux和reflux的介绍,下次再来讲讲redux的介绍。

参考

http://blog.krawaller.se/posts/react-js-architecture-flux-vs-reflux/
http://blog.krawaller.se/posts/reflux-refinement/
http://www.cnblogs.com/lovesueee/p/4893218.html
http://segmentfault.com/a/1190000002793786?utm_source=tuicool#articleHeader16