一种模式,用来强化单向数据流(unidirectional data flow)
剥离数据层,让数据可预测(React让UI可预测,Flux让数据可预测)
具体做法:
作用:
产生action 传递action update state
view交互 -----------> dispatcher -----------> stores --------------> views
其中dispatcher
全局只有一个,store
可以有多个。dispatcher
只负责分发/传递action
,action
到具体state
变化之间的映射由store
维护,所以store
不是单纯的状态集model
,还包含根据action
更新state
的逻辑。再往后就是state
到view
的联系,与数据绑定的具体实现有关,比如React里通过触发事件来通知更新(隐式setState()
)
业务逻辑大多在store
里,另一小部分交互相关的、异步操作相关的在view
(比如React组件)里
业务中经常有级联更新,比如交互操作把一条消息标为已读,要更新消息列表中该条消息的展示样式,还要把未读消息数量减一,级联更新让单向数据流变得不再清晰。Flux通过约束必须在顶层触发action
来避免这种情况,一次view
交互触发一组action
(把级联action
打平,并把级联关系收在顶层,与交互操作直接相关)。而不是一次view
交互触发一个大action
,大action
触发下面的级联action
由store
来完成控制反转,store
不提供setXXX()
来允许外部影响内部state
,唯一的方式是通过在dispatcher
上注册的回调拿到外部数据,自己更新内部state
,保持清楚的关注点分离
flux-simple-f8-diagram-explained
中心枢纽,所有数据流都要过这里,有一张回调注册表,与各store
建立联系。dispatcher本身只负责把action
传递给所有store
,每个store
在dispatcher
注册自己并提供一个回调,dispatcher
收到action
后,所有已注册的store
都将通过各自的回调拿到action
及其携带的数据
应用规模较大的时候,dispatcher
会变得复杂一些,还要管理各store
之间的依赖关系(按顺序调用各store
注册的回调),store
可以通过显示声明等待其它store
更新完成后再更新自己
包含应用状态和逻辑,角色相当于MVC里的重M,但管理一堆state
,而不像ORM里model代表一条数据记录,与Backbone里的collection
也不同,只是简单地管理一组ORM风格的对象
一个store
负责管理应用某块功能对应的内部状态,也就是说,store
不是按具体数据模型(ORM model),或者类型(Backbone collection)来分的,而是按业务功能划分。比如ImageStore负责记录一组图片的状态,TodoStore负责记录一组to-do item,这样,store
在数据上表示model集,在逻辑上表示一块单一功能
store
在dispatcher
上注册的回调接受一个action
参数,store
里面是一个switch
语句,根据action
的type
分发给具体state
更新方法,store
更新完毕后,通过广播事件来告诉view
某些状态变了,对应的view
取新的状态更新自己
一些特殊的view
监听来自自己依赖的store
的广播事件,这些叫view
叫controller-view,含有从store
取数据及向下传递给后代view
的逻辑,一个controller-view通常对应页面上的一块逻辑内容,像view
的逻辑分组一样
controller-view接到来自store
的事件后,先通过store
暴露的getter
取新数据,然后调用自己的setState()
或者forceUpdate()
,触发render()
,render()
触发后代的render()
通常把一大块state
向下传递,下面各取所需,是为了减少需要管理的状态(不做细粒度状态切分)。相对于顶层controller从外部更新状态,这样能保持后代的功能尽量纯净
一般用工具方法来包装action
的生成、注册到store
的过程,内部维持store
与action
的联系(通过action
的type
)
action
也可能来自别处,比如服务端,数据初始化时,服务返回错误码或者服务数据更新了,通过触发action
来同步视图
action
分发/传递和store
内部更新state
都是同步的,异步操作的话,完成的时候手动触发action
,整个机制不帮忙管理异步操作
让应用的信息流非常明确,bug场景对应的state
向上追溯到store
,到对应action
,再到view
层触发action
的点,过程中所有环节都是同步的,那么action
对应的state
就是可预测的,不存在时序上的意外
store
自己内部更新state
,而不是从外部更新,这样其它部分都不需要知道具体的state
变化,状态变化只与store
有关。而store
只接收action
,想对store
做单元测试的话,只需要给一个初态,再丢过来一个action
,然后看终态是否符合预期即可
store
要根据action
更新state
,这样一个action
就相当于一组state
更新操作的名字,有了语义含义,action
不知道怎样更新状态,但描述了预期结果,是相对稳定的(很少需要修改action
,因为仅描述应用的某项功能),比如MARK_THREAD_READ
希望把某条消息置为已读
额外的语义信息有利于追踪状态变化,通过调试工具就能让状态变化可追踪,比如Redux DevTools
不允许一个action
触发另一个action
,以避免级联更新带来的调试复杂度,所以action
是“原子级”的,没有复杂的层级关系
在最佳实践部分,也就是Flux的道德约束
getter
,不给setter
dispatcher
的特定action
作出响应维护内部状态,且只在内部更新状态,关注特定action
,数据变化时无理由触发change,其它时候不触发,除非是dispatcher
引发的
setter
(比如应该是select-page
而不是set-page-id
)view
的React组件store
的信息,存到自己的state
里props
和UI逻辑其实就是controller-view,与普通view
的区别如上所述
普通的view
,没什么特别的