今年FB的React在前端界受到了广泛的关注,广大码农纷纷投入到学习React的阵营中,我也不例外。React标榜自己只是个UI,并不是一个完整的MVC框架,所以一时出现了很多基于React的MVC框架,其中以flux、reflux、redux最为关注。笔者也没在实际项目中使用过React或者任何框架,所以只是想通过todomvc这个例子,浅显得分析下flux在项目中的来龙去脉,如果描述有误或者不正确的地方还望指出。
Flux介绍
flux主要包括四部分:
- action:动作事件,可以理解为一个鼠标点击或者是键盘操作
- dispatcher:分发者,接受到action后分配到各个store
- store:数据和逻辑部分
- views:视图页面,具有监听store功能的页面也称为controller-views
他们之间的联系可以用官网的上图来说明,呈单向数据流环状流程。现在看不懂这张图不要紧,我们可以先来看看代码,等看完todomvc这个demo应该能更好的理解这个图了。
views
整个demo主要划分为四个组件,一个是最外面红色部分的大容器,用来容纳其余的子组件。里面包含Header
、MainSection
、Footer
三个子组件,三个子组件内部又包含了更小粒度的子组件。如下图所示:
那先来看下TodoApp
这个入口文件做了什么,初始状态获取了存在store中的数据,并把数据通过this.props
的方式传递给子组件MainSection
。等到render完组件后添加对store的事件监听,以及卸载组件后移除对store的监听。所以这个位于最顶层的view,也是一个controller-views。
action
接下来看下三个子组件的内部做了什么,除了加载各自内部子组件及数据传递外,我们可以看到凡是涉及到鼠标或者键盘操作的地方,都去调用了TodoAction
中的方法。在TodoAction
中包含了所有的操作,包括新建、更新、切换及删除。所以这个TodoAction
正是上面流程图中对应的Action Creators
,他的作用主要是负责接收所有的事件,并且把action传递给分发者dispatcher
。
dispatcher
当dispatcher接收到action后包装成为一个payload
,包括actionType
值及其他业务数据,并且调用dispatch方法。在dispatcher内部主要包括四个方法:register
,unregister
,waitFor
,dispatch
。payload
的状态也可以分为四个状态:
- 未开始:
Dispatcher_isPending = false
- 开始:
Dispatcher_isPending = true
- 分发中:
Dispatcher_isDispatching = true
- 结束分发:
Dispatcher_isHandled = true
这里dispatch调用了通过register
方法注册的callback函数。如果此时注册了多个store,dispatch会遍历所有的callback函数,直到找到目标为止,数据一多有些资源浪费。
store
在这个demo中,入口文件TodoApp
主动调用了TodoStore中的register方法,主要是用来注册dispatcher
的回调函数。回调函数接收到从action传过来的数据后,通过switch方法来匹配到具体的业务操作中去。需要注意的一点是,这里store会收到所有action的通知,只能通过类型判断,来匹配自己所需要的。等做完操作后,store必须emit一个事件出来,因为前面提到的,最顶层的controller-views已经添加了对store的监听用来更新state,只有这样数据更新后才能体现到页面上来。这样再回过头去上面那张流程图,是不是觉得很清晰了呢。
小结
我们可以把flux整个流程归结为以下几点:
- View 告知 Action Creator 准备一个 action
- Action Creator 做好 action 并将其发送给 Dispatcher
- Dispatcher 按照顺序将 action 传递给 store。每一个 store 都会受到所有的 action 通知,然后自行觉得是否对这个 action 做出响应,更新 state
- 一旦 store 更新 state 完毕,就会告知订阅了该 store 的 controller view
- 这些 controller view 就会向 store 请求更新了的 state
- 从 store 中获得 state 之后,view controller 将会让它所管辖的子 view 渲染新的 state
flux其实就是一个订阅发布模式,但与传统的pub/sub模式区别在于,flux的每一个store都会收到所有action的通知,需要通过一个type值来过滤响应,然后emit一个事件,让controller view来监听更新view。因为存在每个store都会收到所有action的通知,也让flux成为很多人吐槽的地方,所以有人开始重新造轮子了,去掉了dispatcher这层,让action和store自带了pub/sub功能,对于使用者来说代码更为简洁了。后面我将来介绍下reflux和redux是怎么实现这个过程的。