[译]看漫画理解Flux

原文: A cartoon guide to Flux

作者: Lin Clark

原文发布时间: 2015.09.29

译文同时发布于: icyfish’s blog

Flux是现在网页开发领域最流行但很少人对其有深刻理解的一个概念. 这篇文章会从大多数人都能理解的角度对Flux进行解释.

问题

首先, 我想说明Flux的出现是为了解决了什么问题. Flux作为一种架构模式, 其作用是处理应用中的数据. Facebook技术团队为了解决工作过程中遇到的一些问题, 开发了Flux和React, 很多人将它们结合使用, 当然这两者也可以独立使用.

在他们遇到的问题中, 比较典型的一个就是推送的漏洞. 用户登录Facebook时, 会看到信息框的推送小红点. 但当点击信息框, 推送小红点消失, 可实际上用户并没有新信息需要查看. 然后浏览站点几分钟后, 推送通知又会重新出现, 用户就需要为实际上并不存在的新信息多次点击信息框. 正是推送的漏洞导致上述情况的出现.

不只是用户遭遇了推送不断出现的循环, Facebook的开发团队也遭遇了漏洞不断修复又发生的循环, 这一秒问题被解决, 下一秒漏洞又出现, 如此循环往复.

于是Facebook团队决定想出一种方法一劳永逸地解决这个问题. 他们打算开发一个可预测(状态)的系统, 以确保问题不会不断出现.

根本问题

他们发现, 数据在应用中传递的方式是导致该漏洞的根本原因.

注: 这是我从Facebook团队的演讲中分享的简化版本例子中搜集到的资料, 实际的架构必定与此有所差异.

他们让存储数据的模型传递数据到视图层以渲染数据.

用户操作在视图层发生, 因此视图有时候需要根据用户输入进行更新. 而且在某些情况下, 视图还负责更新其它视图.

除了以上的几种情况, 这些行为还可以引发一系列的其他改变. 这就像是游戏Pong一样, 你很难预测球将会停在哪个地方, 甚至是直接跃出屏幕.

另外, 这些改变还可能以异步的形式发生. 一种改变会引发多种其它改变, 就像是在游戏Pong中扔出许多球, 导致球到处乱飞无法控制.

总的来说, 这样的架构使得追踪数据流变得极为困难.

解决方案: 单向数据流

于是, Facebook决定尝试一种不一样的架构, 其中数据只能单向流动, 当我们需要插入新数据时, 数据流动又从头开始. 他们将这个架构叫做Flux.

实际上这个架构模式超级酷, 不过仅从图片上很难看出来.

一旦我们理解了Flux, 就能明白上述图表所表达的意思. 如果你完全不了解Flux, 就开始阅读文档, 那么这个图表或许对你不会有太大帮助, 然而这个图表的作用却是辅助开发者理解文档, 在我们开始深入学习如何进行某些具体的操作之前, 此图表应该让我们对Flux系统有个整体的把握.

然而, 真正帮助我更好地理解Flux的并不是这个图表, 不过我自己想出了个好办法, 我将这个系统(Flux)的工作想象成几个不同的人物进行团队合作, 实现一个共同的目标. 这样的思考方式让我对Flux有更深入的理解. 现在我希望把我的想法, 我想象中的几个人物介绍给大家.

人物介绍

在详细解释这些人物的分工之前, 我先简单介绍一下它们.

The action creator

第一个人物是action creator(动作创建者). 它负责创建action, 应用中所有的改变和交互都需要经由它. 当你想要改变应用的状态(state)或渲染不同的视图, 就要”发射”一个action.

我把action creator看做电报员. 我们把想要传送的信息告知电报员, 电报员帮我们将信息转化成系统其它部分可以理解的格式.

action creator创建的action包含type(类型)和payload(负载)属性. 我们会预先在系统中定义许多action, 这里type属性值就是其中一种action(系统中的action一般来说是一系列的常量). 例如MESSAGE_CREATE或者MESSAGE_READ就可以是action的type值.

让系统的一部分知道在应用中可能发生的所有action有个好处: 刚接触项目的开发者一打开action creator文件, 就可以看到全部API, 了解系统中所有可能发生的状态变化.

一旦创建了action信息, action creator就会将action传送给dispatcher(调度员).

The dispatcher

dispatcher(调度员)是回调函数的登记处, 很像是在电话交换台的电话接线员, 它有一份名单列表, 里面记录着所有需要接收action的store, dispatcher接收到action creator的传送过来的action后, 会将其分配给不同的store.

dispatcher执行操作的方式是同步的, 这样的方式解决了我们之前提到的Pong游戏中的多个球问题. 如果我们要在store之间设置依赖, 即在某个store更新前需要另一个store先执行更新, 那么可以让dispatcher通过waitFor()指令安排好两个store的更新情况.

Flux的dispatcher与许多其它架构中的dispatcher有所差异. 不管action的type值是什么, action都会被发送到已经在dispatcher处注册过的所有store中. 这表明store不只订阅(subscribe to)一部分action, 而是订阅了全部action, 之后再筛选出需要处理和不需要处理的action.

The store

接下来是store. store里储存着应用的所有state, 这些state改变的逻辑也同时储存在store中.

我把store看作是控制欲过强的领导者, 它亲自控制所有state变化. 我们不可以直接要求它改变state, store里没有setter, 如果我们想要让state执行改变, 必须通过action creator/dispatcher这一途径提交action以改变state.

之前提到过, 如果store在dispatcher处已注册, 所有action都会被分发给所有已注册的store. 在store内通常有个switch声明, 根据action的type值决定store是否需要处理该action. 如果是需要处理的action, store会根据action的具体信息更新state.

一旦store完成state的更新之后, 会发出change事件, 以通知控制器视图(controller view)state已完成更新.

The controller view and the view

视图(view)负责接受state并渲染, 同时还负责接收用户输入.

视图就像是展示者, 它并不理会在应用中发生的其它过程, 只在意传到它手中的数据, 并将这些数据转化成用户可以理解的形式并展示出来(通过HTML).

controller view(控制器视图)则像是store和view之间的中介. 当state发生改变时, store会通知控制器视图, 控制器视图接收更新后的state, 将state传递给控制器视图下的所有视图.

如何协作

现在我们来看看这些人物是如何协作的.

配置

首先是初始配置: 只发生一次的应用初始化.

1.应用中的全部store告知dispatcher: 一旦接收到action, 需要通知它们.

2.之后控制器视图向所有store请求最新的state.

3.当store将state传送给控制器视图时, 控制器视图将state传递给它底层的视图(views)以进行渲染.

4.当state有更新时, 控制器视图也会收到store的通知.

数据流

配置结束后, 应用就准备好接受用户输入了. 现在我们让用户执行一些操作来触发一个action然后观察数据流.

1.view通知action creator开始准备创建一个action.

2.action creator转化好action的格式后将其传送给dispatcher.

3.dispatcher将action逐一传送给store. 对于所有的action, 每一个已注册store都会收到通知. 然后store会判断需要对哪些action进行处理, 那些不需要处理, 并且根据action来改变state.

4.state更新好之后, store会通知订阅该store的所有view controller.

5.这些view controller接收到通知后, 会向store请求更新完成的state.

6.store给出state后, view controller让它控制范围下的所有view根据更新的state重新渲染页面.

以上就是我对Flux架构的理解. 希望能够帮到大家.

参考

推荐阅读: 看漫画理解Redux(英文, 中文)

注: 初学redux时看到的文章, 想要通过翻译深入理解, 因此翻译的质量可能不是太好, 内容会在之后的学习过程中不断改进.