天天看点

我理解的 Flux 架构

之前 review 业务代码的时候就一直想说写一篇自己对 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。

因为“正规”

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

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

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

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

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

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

我理解的 Flux 架构

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

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

首先 store 是通过 creator 创建出来的

每个 store 都有自己的 state 用来记录当前状态

在创建 store 的时候,通过 reducer 把 state 和 action 的关系建立起来

后期通过在 store 对象上 dispatch 不同的 action 达到对 state 的修改

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

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

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

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

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

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

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

中心化管理数据,避免数据孤立,一旦数据被孤立,就需要通过其它程序做串联,导致复杂。这是避免各路行为乱改数据导致混乱的一个潜在条件,或者说这是一个结论。

把行为做个归纳,抽象度提高,不管是用户操作导致的,还是从服务器 pull 过来的,还是系统本身操作的。

把修改数据的操作做个归纳,颗粒度变大,大到纯粹“无状态”的极限。

另外一个没有被过多谈论的细节,就是从 model 到 view 要简单直接,这一点各路架构都是有共识的,就不多说了。

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

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

components,即组件化,把视图分解开

通过 computed options 简化 data 到 template 的对应关系

通过 methods options 明确各路行为的抽象

通过双向 computed options 增大了对 data 操作的颗粒度

部分 methods options 也可以用来完成纯粹的 data 操作,增大对 data 操作的颗粒度

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

数据有 components 之间的树形关联,但是修改起来是分散的

相应的 computed、methods 也应该不是分散的,需要改造

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

中心化的 store,所有 components 都共用一份数据,即一份 state;更复杂的情况下,定义有限的几种 getters,用在 computed options 中

定义有限的几种 mutations (类比从 dispatcher 到 store 的约定),可以直接用在 methods options 中;更复杂情况下,定义有限的几种 actions (类比从各路行为到 dispatcher 的约定),用在 methods options 中,背后调用的是各种定义好的 mutations。

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

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

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

我理解的 Flux 架构

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

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

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

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

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

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

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

真的没有代码……

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

<a href="https://github.com/reactjs/redux">https://github.com/reactjs/redux</a>

<a href="https://github.com/vuejs/vuex">https://github.com/vuejs/vuex</a>

<a href="https://github.com/jinjiang/weex-x">https://github.com/jinjiang/weex-x</a>

谢谢

继续阅读