β

Flux

黯羽轻扬 7 阅读
 

一.定位

一种模式,用来强化单向数据流(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

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

维护内部状态,且只在内部更新状态,关注特定 action ,数据变化时无理由触发change,其它时候不触发,除非是 dispatcher 引发的

action

container

其实就是controller-view,与普通 view 的区别如上所述

view

普通的 view ,没什么特别的

参考资料

 
作者:黯羽轻扬
原文地址:Flux, 感谢原作者分享。
Redux GraphQL

发表评论