 
                
最近在学习 Redux 的相关知识,然后在学习的过程中又发现了一个与它关系十分密切的 Flux,而且除了这两个之外,还有一个 React-Redux,为了弄清它们三者之间的差异,所以就打算抽点时间来整理整理 Redux、Flux 和 React-Redux 这三者的关系与区别,我们就先从一切的起源 Flux 开始看起
什么是 Flux
Flux 是 Facebook 用于构建客户端 Web 应用程序的基本架构,我们可以将 Flux 看做一种应用程序中的数据流的设计模式,而 Redux 正是基于 Flux 的核心思想实现的一套解决方案,Flux 应用中的数据以『单一方向流动』的,它有以下几个特点
- 视图产生动作消息,将动作传递给调度器
- 调度器将动作消息发送给每一个数据中心
- 数据中心再将数据传递给视图
也就是下图当中所示的流程

我们可以将上图简化为以下流程
| 1 | View(视图层) ==> Action(请求层) ==> Dispatcher(传输层) ==> Store(处理层) ==> 最后再次回到 View | 
比如用户在视图上(view)点击了一个按钮,即发送了一个 action,然后 action 发送到 dispatcher 中(调度器),dispatcher 来分配这个 action(比如要指派给谁去做任务)给 store(在一个 Flux 结构中,store 可以有多个,注意和 React-Redux 区分),在 store 中的作用就是存储并修改数据,然后传递给 view 进行渲染(渲染到虚拟 DOM 当中),单一方向数据流还具有以下特点
- 集中化管理数据,常规应用可能会在视图层的任何地方或回调进行数据状态的修改与存储,而在 Flux架构中,所有数据都只放在store中进行储存与管理
- 可预测性,在双向绑定或响应式编程中,当一个对象改变时,可能会导致另一个对象发生改变,这样会触发多次级联更新,对于 Flux架构来讲,一次action触发,只能引起一次数据流循环,这使得数据更加可预测
- 方便追踪变化,所有引起数据变化的原因都可由 action进行描述,而action只是一个纯对象,因此十分易于序列化或查看
Flux 的工作流
当我们在使用 MVC 或者 MVVM 架构设计模式的时候,有一个缺点,就是当项目越来越大,逻辑越来越复杂的时候,数据间的流动就会显得十分混乱,而 Flux 就是致力于解决数据有序传输问题的架构设计模式,其中最大的哲学就是『数据是单向流动的』
下面我们就来了解一下 Flux 当中的工作流,可以如下图所示

在 Flux 中我们可以看到会有以下几个角色的出现
- dispatcher,调度器,接收到- action并将它们发送给- store
- action,动作消息,包含动作类型与动作描述
- store,数据中心,持有应用程序的数据,并会响应- action消息
- view,应用视图,可展示- store数据,并实时响应- store的更新
下面我们就来分别看看它们各自的作用
Dispatcher
- dispatcher接收- action,并且要把这些- action分派给已经注册到- dispatcher的- store上
- 所有的 store都将接收所有的action
- 在每个 App中,应该确保只有一个dispatcher的实例
Store
- store是在- App中持有数据的东西,- stores将要在- App的- dispatcher身上注册,以确保它们可以接收- actions
- 存在 store中的数据只能够因为响应action才能有所改变
- 在 store中不能够有公共的setter函数,仅能够有getter函数
- stores决定了它们愿意响应哪些- actions
- 无论什么时候,store中的数据改变了,就会触发一个change事件
- 在一个 App中可能有很多store
Action
- action定义了我们- App中内部的- API
- 它们捕获所有可能改变 App的任何途径、方法
- 它们是简单的 JSON对象,并且要有type属性,和其他一些数据属性,也就是下面这样
| 1 | { | 
- action应该具有一个语义化的命名,比如上面我们定义的这个删除操作的- action,我们一眼就可以看出需要执行的是删除操作,它对应的- id为- 123
- 所有的 store都将接收同一个action,并且通过这同一个action,store会知道它们要清除和更新哪些数据
Views
- 从 store中来的数据将被展示在view上
- view层可以使用任何框架
- 当一个视图想要使用从某一个 store中来的数据,它必须订阅subscribe(订阅)一下该store的change事件
- 当 store发射(emit)了change事件,此时view就能够得到新的数据并且重新渲染
- 如果一个组件要使用 store,但是没有订阅这个store,就会出现问题(bug)
- action最常见的产生原因实在- App的某一个部分因用户的交互行为,而被此- view- dispatch(派发) 出来了
什么是 Redux
在知道了什么是 Flux 以后,我们再来看看 Redux 的相关内容,简单来说,Redux 就是 Flux 思想在 React 当中的实现,所谓的 Redux 可以简单的理解为一个可以预测状态的 JavaScript 的 App 容器,而 App 中的全部 state 都被存储在一个单独的 store 中,形式是 object-tree(JSON),唯一更改 state 的途径就是 emit 一个 action,这个 action 描述了发生了什么
为了指定这些 actions 如何改变 state tree,必须书写简单的、纯净的 reducers,所谓的纯净的 reducers 就是类似下面这样伪代码,它不继承任何东西,并且无论何时返回的值都是固定的
| 1 | function reducers(state, action) { | 
上面就是一个 reducer,是一个纯函数,接收 state 和 action 两个参数,返回新的 state 表达式,如果有使用过 Flux,在这里我们可以发现有一个重要的区别
即在
Redux当中没有dispatcher的概念(store自己负责dispatch某个action到自己身上),也不允许有多个store,所以一般来说,Redux比较适合用于有强的全局数据概念的Web应用(比如商城,购物车等)
Redux 中只有一个唯一的 store,使用唯一的 reducing function,随着项目增长的时候也不要去增加 store,而是应该切割当前的 store 为一个个小的 store,即 store 应该只有一个,类似于 React 当中只允许使用一个根节点,但是根节点是由众多的节点组成,我们下面将会分别进行讨论
为什么要用 Redux
这个需要视当前的使用场景来决定的,当然除了 Redux 还有 Flux、Reflux、Mobx 等状态管理库可供选择,下面就是一个实际场景,比如在控制台上记录用户的每个动作
| 1 | // 后端,比如使用 Express 中实现一个简单的 Logger  | 
然后现在又需要在上述需求的基础上,记录用户的操作时间
| 1 | // 后端,只需要稍微修改一下原来的中间件即可 | 
又比如说,在正式上线的时候,把控制台中有关 Logger 的输出全部去掉,亦或是自动收集 bug,很明显的可以看出前后端对于这类需求的处理竟然大相径庭,原因在于,后端具有统一的入口与统一的状态管理(数据库),因此可以引入中间件机制来统一实现某些功能,而前端也可以使用 MVC 的开发思维,将应用中所有的动作与状态都统一管理,让一切有据可循
Store
我们首先要区分 store 和 state 之间的区别,state 是应用的状态,一般本质上是一个普通对象,例如我们有一个 Web App,包含计数器和待办事项两大功能,那么我们可以为该应用设计出对应的存储数据结构(应用初始状态)
| 1 | /』应用初始 state『/ | 
而 store 则是应用状态 state 的管理者,包含下列四个函数
- getState(),获取整个- state
- dispatch(action),触发- state改变的【唯一途径】
- subscribe(listener),可以理解成是- DOM中的- addEventListener
- replaceReducer(nextReducer),一般在- Webpack Code-Splitting按需加载的时候用(使用较少)
二者的关系是 state = store.getState()
- Redux,规定,一个应用只应有一个单一的- store,其管理着唯一的应用状态- state
- Redux,还规定,不能直接修改应用的状态- state,也就是说,下面的行为是不允许的
| 1 | var state = store.getState() | 
若要改变
state,必须dispatch一个action,这是修改应用状态的不二法门
- 针对 action,暂时只需要记住,action就是一个包含type属性的普通对象,例如{ type: 'INCREMENT' }
- 而 store,我们需要调用Redux提供的的createstore()方法,如下
| 1 | import { createStore } from 'redux' | 
- 针对 reducer,暂时只需要记住,reducer是一个 函数,负责更新并返回一个新的state即可
- 而第二个参数 initialState主要用于前后端同构的数据同步(详情请关注React服务端渲染)(可暂时不用管)
Action
action(动作)实质上是包含 type 属性的普通对象,这个 type 是我们实现用户行为追踪的关键,例如增加一个待办事项的 action 可能是像下面一样
| 1 | { | 
action 的形式是多种多样的,唯一的约束仅仅就是包含一个 type 属性
| 1 | // 下面这些 action 都是合法的,但就是不够规范 | 
具体规范可见 flux-standard-action
Action Creator
Action Creator 是 action 的创造者,本质上就是一个函数,返回值是一个 action(对象)(可以是同步的,也可以是异步的),例如下面就是一个新增一个待办事项的 Action Creator
| 1 | var id = 1 | 
简单来说,Action Creator 就是用于绑定到用户的操作(比如点击按钮等),其返回值 action 用于之后的 dispatch(action)
Reducer
需要注意的是,reducer 必须是同步的『纯函数』,简单来说分为以下三步
- 用户每次 dispatch(action)后,都会触发reducer的执行
- reducer的实质是一个函数,根据- action.type来更新- state并返回- nextState
- 最后会用 reducer的返回值nextState完全替换掉原来的state
几个需要注意的地方
- 所谓的更新并不是指 reducer可以直接对state进行修改
- Redux规定,须先复制一份- state,在副本- nextState上进行修改操作
- 例如,可以使用 lodash的cloneDeep,也可以使用Object.assign/map/filter ...等返回副本的函数
例如下面这个示例
| 1 | var initState = { | 
简单的理解就是,reducer 返回什么,state 就被替换成什么
Redux 的整体流程
- store由- Redux的- createstore(reducer)生成
- state通过- store.getState()获取,本质上一般是一个存储着整个应用状态的对象
- action本质上是一个包含- type属性的普通对象,由- action Creator(函数) 产生
- 改变 state必须dispatch一个action
- reducer本质上是根据- action.type来更新- state并返回- nextState的函数
- reducer必须返回值,否则- nextState即为- undefined
- 实际上,state就是所有reducer返回值的汇总
大致流程如下所示
| 1 | Action Creator  | 
Redux 官方示例剖析
下面我们就通过一个例子来深入的了解一下 Redux 的工作流程,示例参考的是官方提供的 counter-vanilla(见 redux/examples/counter-vanilla/index.html)
| 1 | // reducer | 
action 所对应的字段一般都是约定成俗的使用大写字母来进行表示,它描述了一个 action 如何使当前 state 改变为下一个 state,state 的形式取决于你,它可以是一个基本类型值,可以是一个数组,也可以是一个对象等等,唯一需要注意的就是,永远不要去更改当前的 state,而是应该返回一个新的 state 对象
| 1 | var store = redux.createstore(counter) | 
首先创建一个 Redux 的 store,用它来持有 App 的 store,store 的 API 及其简单,就三个,subscribe,dispatch 和 getState
- subscribe,让- store去注册一个视图
- dispatch,分发一个命令
- getState,返回一个状态
| 1 | store.subscribe(render) | 
使用 store 的 subscribe() 方法,将 store 订阅了视图,render 是一个函数,其实简单来说就是,每次当 state 变化的时候就会执行该函数,通常情况下是与 React 来配合使用,调整上面的示例,添加一个每次点击增加 2 的按钮
| 1 | // reducer | 
调整示例,添加一个输入框,然后点击的时候加上输入框内的值
| 1 | function counter(state = 0, action) { | 
综合以上示例,点击按钮的时候,使 store 去 disptch 一个命令,这时需要注意了,数据存储在 store 中,然后 store 给自己 dispatch 了一条命令,然后自己再去识别给自己发送的命令(case),然后改变存储在自己 store 中的 state(return)
之所以这样设计,就是因为在 reducer 中可以看见整个程序的 state 会发生怎样的变化,虽然不知道什么时候会变化,但是知道其可以做出什么样的变化,知道其不能够做出什么样的变化,这就是 Redux 的哲学,让 state 可以被预期,这也就是下面的 reducer 存在的意义
| 1 | // reducer 清单 | 
综上
- 我们不是直接去修改 state,而是指定了一个简单的JSON对象(类似指令,type)去描述我们想要什么事情发生,这个JSON称之为action
- 然后声明一个特定的 reducer的函数去指定每一个action要如何改变整个App的store
注意这个『整个』,看下面的示例,我们先将 state 默认值设置为一个对象(不再是简单的数字)
| 1 | // reducer | 
直接使用类似上面的 return state.m + 1 是没有效果的,这时需要返回的是整个 state 的值(建议使用 ES6 中的 ... 运算符)即
| 1 | // reducer | 
 
         
                 
                 
                