我理解的 Flux 架构

本文早些时候发表在 云栖社区 https://yq.aliyun.com/articles/59357

之前 review 业务代码的时候就一直想说写一篇自己对 Flux 的理解和看法,不知不觉也过去蛮久了,于是这周末打起精神写了这么一篇。

这篇文章将谈一些我对 Flux 的理解和个人看法。如果您还不太了解什么是 Flux,请先移步这里

另外文中没有特别大段的代码,以讨论架构设计和背后的道理为主,可能会显得有点枯燥,大家可以选个不太困的时候耐心读读看:)

Flux 中的几个基本概念

这是 Flux 官方提供的一张说明图:

图中有四个名词:

  • View
  • Store
  • Action
  • Dispatcher

下面逐个以我的角度做个讲解:

首先 View 是视图,是用户看得见摸得着的地方,同时也是产生主要用户交互的地方,这个概念在 MVC 和 MVVM 架构中都是有的,有些观点认为虽然这几种架构里都有 View,但是定义不太一致,有细微的差别,我自己觉得这种差异确实是存在的,但在一开始这并不妨碍我们理解 View 这个名词。

然后是 Store,它对应我们传统意义上的 Data,和 MVC、MVVM 里的 Model 有一定对应关系。你问我它们为啥不直接叫 Data 算了,那这就是文化人和小老百姓表达方式的差别。当然了我只是想尽量降低理解成本,尝试用比较通俗的说法把问题说清楚。

然后是 Action,这看上去是一个新概念,实际上我还是能找到一些帮助大家理解的名词,叫做 Event。就是一个结构化的信息,从一个地方传递到另一个地方,整个过程就是一个 Action/Event。

最后是 Dispatcher,多说一句,我觉得正是因为有了 Dispatcher 才让前面三个名词变得有新鲜感。也是理解 Flux 的关键。言归正传,Dispatcher 算是从 Action 触发到导致 Store 改变的镇流器。比一般架构设计里直接在“Event”逻辑中修改“Data”更“正规”。所以土得掉渣的 Event 变成了 Action,土得掉渣的 Data 变成了 Store,土得掉渣的 View 仍然是土得掉渣的 View。

为什么多了 Dispatcher,这些 Store、View、Action 就变得神奇了呢?

因为“正规”

传统 MVC 被 Flux 团队吐槽最深的,表面上是 Controller 中心化不利于扩展,实际上是 Controller 需要处理大量复杂的 Event 导致。这里的 Event 可能来自各个方向是 Flux 吐槽的第二个点,所以不同的数据被不同方向的不同类型的 Event 修改,数据和数据之间可能还有联系,难免就会乱。

所以和 Dispatcher 配合的 Store 只剩下了一个修改来源,和 Dispatcher 配合的 Action 只剩下了约定好的有限几种操作。一下子最混乱的地方变得异常“正规”了。架构复杂度自然就得到了有效的控制。

另外还有一个蛮好理解的点是:Action 不仅仅把修改 Store 的方式约束了起来,同时还加大了 Store 操作的颗粒度,让琐碎的数据变更变得清晰有意义。

另外,这两个地方抽象之后数据操作变得“无状态”了,所以可以根据 Action 的历史记录确定 Store 的状态。这个让很多撤销恢复管理等场景成为了可能。

综上所述,在 Flux 架构中,数据修改的颗粒度变大,更有语义;上层数据操作的行为更抽象化,碎片化程度降低。

Flux 架构是 React 技术栈独占的吗?

不是,只要在传统架构的基础上注重对数据操作和用户/客户端/服务器行为的抽象定义,Flux 架构中提到的各种好处大家都享受得到。

image

我们就拿被 Flux 黑得最惨的那个“一大堆 V 和一大堆 M 只有一个 C”的例子好了,图中每个 View 找到不一样 Model 进行操作时,我们把这些操作抽象成 Action,然后通过中心化的逻辑找到相应的 Model 完成修改,其实就是 Flux 了。这里抽象出来的 Action 一定要和图中 Controller 能够接受到 Action 一样,没有什么特殊的地方。

基于这样的理解,Redux 提出了另外的对 Flux 架构的理解:

  1. 首先 Store 是通过 Creator 创建出来的
  2. 每个 Store 都有自己的 state 用来记录当前状态
  3. 在创建 Store 的时候,通过 Reducer 把 state 和 action 的关系建立起来
  4. 后期通过在 Store 对象上 dispatch 不同的 action 达到对 state 的修改

本质上同样是对数据操作和上层行为的抽象,另外从实现层面更加 functional。

Vuex 是基于 Vue.js 的架构设计,稍后再展开说我的看法。

Flux 架构有什么不为人知的坑吗?我们就像看人黑 Flux!

(咳咳咳~~~ 这个问题我得谨慎回答)

我觉得 Flux 架构没有把一个事实告诉大家,就是它的 Store 是中心化的,Flux 用中心化的 Store 取代了它吐槽的中心化的 Controller。

我看了一些基于 Flux/Redux/Vuex 架构的实现,基本上多个 Store 之间完全解耦不建立任何联系是不可能的——除非它们完全从数据行为各方面都是解耦的——这种程序用什么架构都无所谓的坦白讲。

为什么中心化的 Store 无人吐槽呢?因为中心化的数据复杂度绝对低于中心化的行为控制。你甚至没有意识到它是中心化的,这其实从另外一个侧面就证实了这一点。

所以我觉得透过 Flux 看架构的本质:这里不算是坑或吐槽,我更想说的是,放下 Flux 这把锤子,我们该怎么看世界,怎么看待自己每天在设计和架构的软件。

  1. 中心化管理数据,避免数据孤立,一旦数据被孤立,就需要通过其它程序做串联,导致复杂。这是避免各路行为乱改数据导致混乱的一个潜在条件,或者说这是一个结论。
  2. 把行为做个归纳,抽象度提高,不管是用户操作导致的,还是从服务器 pull 过来的,还是系统本身操作的。
  3. 把修改数据的操作做个归纳,颗粒度变大,大到纯粹“无状态”的极限。
  4. 另外一个没有被过多谈论的细节,就是从 Model 到 View 要简单直接,这一点各路架构都是有共识的,就不多说了。

在这几个方面,如果一个架构师能够做到极致,去TM的各种架构缩写,用哪个都一样。

Vuex 怎么样?

我先说我觉得 Vue.js 怎么样,Vue.js 天生做了几件事:

  1. components,即组件化,把视图分解开
  2. 通过 computed options 简化 data 到 template 的对应关系
  3. 通过 methods options 明确各路行为的抽象
  4. 通过双向 computed options 增大了对 data 操作的颗粒度
  5. 部分 methods options 也可以用来完成纯粹的 data 操作,增大对 data 操作的颗粒度

所以 Vue.js 本身已经提供了很多很好的架构实践。但这在 Flux 看来还不够纯粹,它缺 2 点:

  1. 数据有 components 之间的树形关联,但是修改起来是分散的
  2. 相应的 computed、methods 也应该不是分散的,需要改造

所以 Vuex 需要做的事情很简单:

  1. 中心化的 store,所有 components 都共用一份数据,即一份 state;更复杂的情况下,定义有限的几种 getters,用在 computed options 中
  2. 定义有限的几种 mutations (类比从 Dispatcher 到 Store 的约定),可以直接用在 methods options 中;更复杂情况下,定义有限的几种 actions (类比从各路行为到 Dispatcher 的约定),用在 methods options 中,背后调用的是各种定义好的 mutations。

这样在 Vue 的基础上,再加上如虎添翼的 Vuex,开发者就可以享受到类似 Flux 的感觉了。

都快说完了都没提“单向数据流”这个词

是的,我觉得这是一个被用烂的词,以至于很多人在求职面试的时候一被问到 Flux 就脱口而出“单向数据流”,几乎当做 Flux 这个词的中文翻译在回答。就好像一说到 Scrum 就脱口而出“看板”一样……

我觉得单向数据流的讲法太过表面,不足够体现出 Flux 的设想和用意。现在一提单向数据流,我脑中第一个浮现的画面其实是这个:

image_jpeg

都快说完了都没提“时空穿梭 (time travel)”这个词

这是数据操作颗粒度变大之后的名词。我觉得它只是个名词,为什么这样说?

所为“时空穿梭”,本质就是记录下每一次数据修改,只要每次修改都是无状态的,那么我们理论上就可以通过修改记录还原之前任意时刻的数据。

大家设想一下,其实我们每次对数据最小颗粒度的、不能再分解的、最直接的操作基本 (比如赋值、删除、增减数据项目等) 都是无状态的,其实我们如果写个简单的程序,把每次直接修改数据的操作记录下来,同样可以很精细的进行“时空穿梭”,但没有人提这个词,因为它颗粒度太细了,没有语义,没有人愿意在这样琐碎的数据操作中提炼“时空”。因为数据操作的颗粒度变大了,所以变得直观,有语义,易于理解,对我们的功能研发和调试有实际帮助,所以才有了“时空穿梭”这个概念。

Weex 什么时候支持 Flux/Vuex?

这是我最后想说的,首先不管有没有 Flux/Vuex,一个好的架构实践已经足以满足日常的研发需求,尤其是在手机上,界面、数据和行为都不会特别复杂。

其次,如果基于 Vue 2.0 来开发 Weex 页面或应用的话,Vuex 是天生支持的,不需要额外做什么。大家如果已经在浏览器中,不论是桌面还是手机上实践过 Vuex,应该是感觉不到任何不一样的。

最后,上周我简单写了个 Vuex 的复刻版,能够在 Weex 的 JS Framework 上工作,这里不想占太多篇幅介绍。坦白讲我希望大家更多的精力在理解 Flux 和 Vue 上。其它问题都是顺理成章的。

总结

这篇文章整理了我个人对 Flux 的理解和个人看法,首先解释一下 Flux 核心的四个名词:View, Store, Action, Dispatcher,然后提出 Dispatcher 在 Flux 架构中的关键位置,并解释为什么 Dispatcher 让其他三者变得更好更“正规”,然后是一些我通过了解 Flux 认识到的背后倡导的架构设计的最佳实践的提炼。

真的没有代码……

……好吧如果一定要看代码可以看看这里

谢谢

向本文提出修改或勘误建议