Flux 与 Redux

Redux 是我们学习状态管理的第二站。在前面的一站中,我们用了一个单独的专栏讲解了 Flux 架构的前世今生。注意:这里我说的是 Flux 架构,当然 Flux 也可以作为一个库来进行状态管理,但是作为一个库来说,它显得是那么的粗糙,更像是提供了一种状态管理的思想,用官网的一句话来描述,就是:

Flux is the application architecture that Facebook uses for building client-side web applications. It complements React’s composable view components by utilizing a unidirectional data flow. It’s more of a pattern rather than a formal framework, and you can start using Flux immediately without a lot of new code.
_
大致的意思就是说:Flux 是 Facebook 用于构建客户端 Web 应用程序的应用程序架构。它通过利用单向数据流来补充 React 的可组合视图组件。它更像是一种**模式**,而不是一种正式的框架,你可以在没有大量新代码的情况下立即开始使用Flux。

下面是两张 Flux 架构模式的图解,第一张是一个简单的描述,第二张将实际应用开发中设计到的概念都展现出来了。

877983-20160913183547211-434347824.png

Flux 架构.svg

由于 Flux 只是提供了状态管理的一种模式,再加上其应用于实际项目中的问题,于是社区出现了各种各样关于 Flux 架构模式的实现框架,其中最引人注目之一的就是本专栏的主角 Redux 了。

Flux 的问题

在具体学习 Redux 之前,我们来说一下 Flux 架构的问题,然后再来看看 Redux 是通过什么方式来优化的。

  • 1、在实际项目中应用相对复杂

Flux 架构本身定义了描述 Store 里面状态变化的 action 和根据 action 修改 Store 里面状态的 Dispatcher,Dispatcher 用来注册修改 Store 的逻辑。为了让 Flux 更好的为我们的项目服务,我们需要通过 EventEmitter 将 Store 状态的改变和触发 view 重绘的逻辑联系起来。这样无意中增加了冗余代码的数量,而且对于每个应用都需要手动创建一个 dispatcher 实例。

对于状态的修改,Flux 并没有提供很好的方案,一般都是对现有 Store 里面状态直接进行修改。虽然 Flux 奉行单向数据流的模式,但是这样对 Store 的直接修改直接导致热加载和时间旅行无法实现,对于问题和 bug 的追溯相对不是很完善 —— 也就是说 Flux 虽然在数据状态的管理上起到了一定的作用,但是还可以做得更好。

  • 3、Flux 设计约定相对松散

在上面我们也提到过我们更新状态是通过直接修改的形式进行的,这样的实现带来了一些功能上的缺失,比如数据状态的追溯 —— 热加载和时间旅行。状态修改后,如何触发 view 的更新,这个需要我们通过 EventEmitter 去实现,这样无疑增加了我们代码的复杂度。而且一个应用可以同时构造多个 Store 用来保存状态数据,这样的设计对于大型应用的构建无疑是不利的

Redux

正因为 Flux 还有很多可以优化的地方,所以 Dan Abramov 受 Elm 的启发,将 Flux 演进成了 Redux。Redux 奉行三条原则:

  • 1、单一数据源

整个应用的 state 都存在于一个 object tree 上面的,并且这个 object tree 只存在于唯一一个 store 中,也就说整个应用有且只有一个 store。这点和 Flux 就不一样,在 Flux 架构模式下,一个应用可以同时构造多个 Store 用来保存状态数据,而 Redux 则强约定整个应用有且只有一个 store,这样的设计对服务端同构应用是非常友好的,来自服务端的状态数据无需做更多的处理即可注入到 store 进行管理。单一 store 的管理更加利于应用的调试,此外利用本地存储技术还可以很轻松的实现 撤销/重做 等功能。

  • 2、State 只读

在 Redux 架构模式中,唯一改变 state 的方法就是触发 action,action 是一个用于描述 state 变化的普通对象。除此之外,并无其他的办法对状态进行修改。由于状态的修改只能通过触发 action 实现,所以修改操作是统一处理的,在 JavaScript 单线程模式中是不用担心竞争状态的。而且 action 只是一个描述 state 变化的普通对象,因此它们可以被日志打印、序列化、储存、后期调试或测试时回放出来。

  • 3、必须使用纯函数进行修改

光有描述 state 变化的 action 还不能去修改 state,为了描述我们是怎么通过 action 修改 state 的,还要编写 reducer。reducer 有一个很独特的性质 —— 纯函数,它会接受原先的 state 和 action,然后返回新的 state,用公式可以描述为:

  1. (prevState, action) => newState

在 reducer 中不要存在可产生副作用的逻辑代码,而且使用返回一个新的 state,不要试图在旧的 state 上进行修改,这样会让你的应用变得混乱。当然,随着应用复杂度的增加,你可以对 reducer 进行拆分,让各个小的 reducer 处理属于自己那部分逻辑的 state tree,最后通过 combineReducers 方法进行合并,这样应用的可扩展性和可伸缩性就会变得很强。

总结

Redux 系列的第一篇文章,首先我们提到了前端状态管理学习的第一站 Flux,并说了一下关于 Flux 的认知和问题,然后引入了对 Redux 的介绍,说到了 Redux 奉行的单一数据源、State 只读和必须通过纯函数 reducer 修改 state 这三大原则。下一系列,我们将介绍关于 Redux 的第一个核心 Store 和 State,并对 Redux 奉行的三大原则进行展开描述。