本文是『horseshoe·Redux专题』系列文章之一,后续会有更多专题推出
来我的
GitHub repo 阅读完整的专题文章来我的
个人博客 获得无与伦比的阅读体验
React的横空出世给前端带来一场革命。其实随React一同开源的还有一个叫Flux的东西。Flux是管理应用状态的一种架构。光凭一个props是无法实现血缘关系疏远的组件之间的状态同步的。虽然context加回调可以勉强做到这一点,但是Flux架构有其更加深远的意义。
你看,从一开始facebook的工程师就知道只凭React无法支撑大型的应用开发。但是为什么称Flux为一种架构而不是一个类库或者框架呢?因为它的出现主要是为了提出一种思想,而不是作为一个真正成熟的类库。facebook的工程师希望社区根据这种思想涌现出多样化的实现方式。
那么,Flux提出的思想是什么呢?
这一切还要从MVC架构说起。
MVC
以前端举例,MVC架构将一个应用分为Model
、View
和 Controller
三部分。Model
代表数据模型,用来存放业务逻辑;View
代表视图,是最终的界面效果;Controller
代表控制器,响应用户的操作。
一个用户操作View
发出指令,Controller
处理该指令并且将处理结果传递给Model
,Model
再将更新后的数据渲染到View
上。
当一个应用越来越复杂时,如何去维护庞大的代码和让新人快速的上手就变的紧迫起来。MVC架构的分层思想就是服务于这一目的,它并不能让应用的性能提升,但是它可以让代码对开发者更友好。不过影响代码对开发者友好度的可不止这一点,更重要的是数据的流向在开发者面前的清晰度。
MVC架构有没有对数据的流向做很好的控制呢?并没有。它有一套理想中的流程,就是上面讲到的视图更新的操作过程,然而View
可不可以直接给Model
发指令?当然可以,并且很多开发者就是这么做的。如果每一层都可以和每一层通信,代码的可读性将变的混乱不堪。
Flux
Flux就是为解决这个问题而生的。MVC架构的问题在于没有严格控制数据的流向,所以facebook的工程师将Flux设计成数据只能走单通道,一个特定的操作才能触发另一个特定的操作。
一个Flux应用包含四部分:
-
Action
,一个动作对象,相当于用户的指令。 -
Dispatcher
,处理动作的派发,相当于MVC架构的Controller
。 -
Store
,负责存储数据,相当于MVC架构的Model
。 -
View
,和MVC架构一样。
用户触发View
上的事件发送Action
,Action
只能通过Dispatcher.dispatch
方法发送出去,Store
更新自身的数据然后View
根据Store
重新渲染。一环扣一环,只能按照这个流程走下去。
这就是我们说的单向数据流。
至此,我们实现了功能的分层和数据的单向流动,代码不再那么混乱了。
Redux
我们的主角是Redux,它也是目前React社区最受欢迎的状态管理框架。实际上,Redux就是Flux的门徒之一。它如此受欢迎,一定是因为它在Flux的基础上做对了什么事情。
Flux解决了单向数据流的问题,那么Redux解决了什么问题呢?
我认为Redux的贡献主要是这两点:
- 推崇唯一数据源。
- 加入函数式编程思想。
状态只读
和只能通过纯函数来改变状态
都是函数式编程的特性,目的就是为了改变状态的过程中不产生副作用。
咱们先来说说唯一数据源是怎么回事?
Flux的设计是将每一块相对独立的状态分别用一个Store来存储。这样的好处是显而易见的,每一个Store的体积都足够小,对象的嵌套不会很深。
坏处是什么呢?
Dispatcher在派发动作的时候,会依次访问每一个Store。这样虽然会损失一些性能,但是Dispatcher的逻辑可以做到极简,它不用知道这个动作应该派发给谁,都给它们发一遍得了。假如Store A对Store B存在依赖关系,那么它们的状态更新顺序就很重要,而Dispatcher哪管这些个儿女情长。于是开发者需要通过Dispatcher注册回调函数返回的token来手动管理Store之间的依赖关系。
这无疑又是对开发者不友好的设计。
所以Redux干脆,只维护一个全局的Store,让你开发者再哔哔哔。正事不干,就知道哔哔哔。
需要注意的是,Redux并没有从机制上阻止开发者使用多个Store管理应用,开发者还是可以作妖的。不过这又回到Flux的老路上去了,对开发者一点好处都没有。Redux选择用思想和约定来约束开发者。
再来说函数式编程。
函数式编程思想的提出最早是为了处理复杂的计算任务。计算任务的要求是什么呢?结果可预期,任务之间不会相互影响。
相同的输入,总是有相同的输出。对应到函数,就是不能修改已有的数据,只要修改已有的数据,理论上就有可能输出不同的结果。那么我确实有修改数据的需求怎么办?生成一个新的衍生数据。
执行某个任务不会对外部产生影响,也就是所谓的没有副作用。
我们想一下,Redux加入函数式编程思想的目的不还是为了让单项数据流更加清晰和单纯么?
Redux给前端带来的另一个重大成果是时间旅行式的调试。开发者可以回到任意时间点的案发现场,看看那个臭虫到底是哪个阴沟里生出来的。
如果你看完本专题,就会发现Redux的作者Dan Abramov从一开始设计Redux就是奔着时间旅行式的调试去的。时间旅行才是Redux写法这么繁琐的罪魁祸首。
所以Flux的历史功绩是为前端引入了一种思想,而Redux仅仅是改进这种思想而已,还算不上一代宗师。不过Redux本来就是Flux的门徒之一,作为门徒能够将师门发扬光大也是一代豪杰了。
Redux专题一览