内容简介:Flux
一.定位
一种模式,用来强化单向数据流(unidirectional data flow)
二.作用
剥离数据层,让数据可预测(React让UI可预测,Flux让数据可预测)
具体做法:
-
用显式数据,不用衍生数据(先声明后使用,不临时造数据)
-
分离数据和视图状态(把数据层抽出来)
-
避免级联更新带来的级联影响(M与V之间互相影响,数据流不清楚)
作用:
-
提升数据一致性
-
易于精确定位bug
-
便于单元测试
三.结构
产生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
单dispatcher
中心枢纽,所有数据流都要过这里,有一张回调注册表,与各 store
建立联系。dispatcher本身只负责把 action
传递给 所有 store
,每个 store
在 dispatcher
注册自己并提供一个回调, dispatcher
收到 action
后,所有已注册的 store
都将通过各自的回调拿到 action
及其携带的数据
应用规模较大的时候, dispatcher
会变得复杂一些,还要管理各 store
之间的 依赖关系 (按顺序调用各 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
一些特殊的 view
监听来自自己依赖的 store
的广播事件,这些叫 view
叫controller-view,含有从 store
取数据及向下传递给后代 view
的逻辑,一个controller-view通常对应页面上的一块逻辑内容,像 view
的逻辑分组一样
controller-view接到来自 store
的事件后,先通过 store
暴露的 getter
取新数据,然后调用自己的 setState()
或者 forceUpdate()
,触发 render()
, render()
触发后代的 render()
通常把一大块 state
向下传递,下面各取所需,是为了减少需要管理的状态(不做细粒度状态切分)。相对于顶层controller从外部更新状态,这样能保持后代的功能尽量纯净
一堆action
一般用 工具 方法来包装 action
的生成、注册到 store
的过程,内部维持 store
与 action
的联系(通过 action
的 type
)
action
也可能来自别处,比如服务端,数据初始化时,服务返回错误码或者服务数据更新了,通过触发 action
来同步视图
四.特点
强制同步
action
分发/传递和 store
内部更新 state
都是同步的,异步操作的话,完成的时候手动触发 action
,整个机制不帮忙管理异步操作
让应用的信息流非常明确,bug场景对应的 state
向上追溯到 store
,到对应 action
,再到 view
层触发 action
的点,过程中所有环节都是同步的,那么 action
对应的 state
就是可预测的,不存在时序上的意外
控制反转(IoC)
store
自己内部更新 state
,而不是从外部更新,这样其它部分都不需要知道具体的 state
变化,状态变化只与 store
有关。而 store
只接收 action
,想对 store
做单元测试的话,只需要给一个初态,再丢过来一个 action
,然后看终态是否符合预期即可
语义化的action
store
要根据 action
更新 state
,这样一个 action
就相当于一组 state
更新操作的名字,有了语义含义, action
不知道怎样更新状态,但描述了预期结果,是相对稳定的(很少需要修改 action
,因为仅描述应用的某项功能),比如 MARK_THREAD_READ
希望把某条消息置为已读
额外的语义信息有利于追踪状态变化,通过调试工具就能让状态变化可追踪,比如Redux DevTools
没有级联action
不允许一个 action
触发另一个 action
,以避免级联更新带来的调试复杂度,所以 action
是“原子级”的,没有复杂的层级关系
五.约定
在 最佳实践 部分,也就是Flux的 道德约束
store
-
缓存数据
-
只暴露用来访问数据的
getter
,不给setter
-
对来自
dispatcher
的特定action
作出响应 -
任何数据变化时都触发change事件
-
只在dispatch过程中才触发change事件
维护内部状态,且只在内部更新状态,关注特定 action
,数据变化时无理由触发change,其它时候不触发,除非是 dispatcher
引发的
action
- 描述用户行为,而不是
setter
(比如应该是select-page
而不是set-page-id
)
container
-
用来控制
view
的React组件 -
基本职能是收集来自
store
的信息,存到自己的state
里 -
不含
props
和UI逻辑
其实就是controller-view,与普通 view
的区别如上所述
view
-
由container控制的React组件
-
含有UI和渲染逻辑
-
接收所有信息和回调作为props
普通的 view
,没什么特别的
参考资料
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。