Redux
是一种前端“架构模式”,是 Flux
架构的一种变种,用来提供可预测的状态管理。虽然常常和 React
一块儿被说起,可是 Redux
却不只仅只能用于 React
,还能够将其运用到其余前端库中,Vue
Angular
甚至是 jQuery
。Redux
只是一种架构模式而已,并无和其余库绑定在一块儿。而 React-redux
就是把 Redux
和 React.js
结合起来的一个库。就像 Vuex
同样,是一个与 Vue.js
结合的 Flux
变种。前端
也许有人会问:为何咱们会须要 redux
呢? 嗯... 确实,咱们必需要先了解咱们为何须要 redux
? redux
的出现是为了解决什么问题?react
那么,咱们来考虑这么一种场景,在你构建的一棵组件树中,有A、B那么两个组件,它们须要共享同一个状态,你会怎么办呢?redux
咱们能够经过状态提高的思路,将该状态提高到附近的公共父组件上面,而后经过 props
把状态传递给子组件,这样就能够在A
、B
组件之间共享数据了。确实能够,可是若是A
、B
的父组件在组件树向上好几个组件的位置呢?就须要将状态经过 props
一级一级往下传递,那么状态的传递路径就会很是长,并且中间组件根本就不须要访问这个状态。并且,若是后续有一个C
组件也要访问该状态而且A
、B
、C
的公共父组件还要往上呢?你就不得不修改以前代码了。很显然,这不是一种很好的解决方案,它会让咱们的代码维护起来很是痛苦。微信
难道就没有其余方法能够解决这个问题吗?其实也有的,那就是 react
的 context
,一个组件只要往本身的 context
里面放了某些状态,那么这个组件的全部子组件均可以直接访问这个状态而不须要经过中间组件的传递,看起来问题解决了嘛。架构
咱们虽然解决了状态传递的问题却引入了新的问题,咱们引入的 context
打破了组件和组件之间经过 props
传递数据的规范,极大地加强了组件之间的耦合性。并且 context
就像全局变量同样,里面的数据能够被子组件随意更改,可能会致使程序不可预测的运行。函数
这时候咱们就该考虑使用 Redux
了,Redux
能够帮你建立应用的共享状态,而且不能随意的更改这些状态。spa
咱们已经了解了为何要使用 Redux
,那么咱们先了解下 Redux
的三个基本概念。设计
咱们能够经过 createStore 来建立 storecode
import { createStore } from 'redux'; const store = createStore(reducers);
在 Redux
中,应用程序只能拥有一个 store
,用来保存整个应用程序的 state
,至关于一个应用程序的共享状态。对象
咱们能够经过 store.getState()
来获取应用程序的当前状态。可是咱们却不能随意的修改状态,咱们只能经过 store.dispatch(action)
来修改状态。
修改完状态以后,咱们但愿能够作些 view
层的改变,这时能够经过 store.subscribe(() => {})
来注册视图变化的回调函数。
Actions
是一个 JavaScript
普通对象,用来描述应用程序中发生的一些事情,也是把数据从应用传递给 store
的惟一途径。
咱们约定,action
内必须使用一个字符串类型的 type
字段来表示将要执行的动做,type
通常会被定义成字符串常量。
const ADD_TODO = 'ADD_TODO' { type: ADD_TODO, data: 'some data' }
咱们除了直接以 JavaScript
普通对象的形式来定义 action
以外,也能够经过函数形式来定义 action
,这个函数被称做 Action
建立函数( actionCreator
)。
const ADD_TODO = 'ADD_TODO'; function addTodo(data) { return { type: ADD_TODO, data } }
这里 action
建立函数 addTodo
很简单,只是返回一个 action
。 咱们能够经过 store.dispatch
来通知须要修改状态
store.dispatch(addTodo('some data'));
咱们已经知道能够经过 action
来修改状态,可是 action
传递过来的只是简单的对象,并无具体处理状态的逻辑,这就是 reducers
要作的事情了。
Reducer
必须是一个纯函数(一个函数的返回结果只依赖于它的参数,而且在执行过程里面没有反作用,这个函数就叫作纯函数)。由于纯函数很是“靠谱”,执行一个纯函数不会产生不可预料的行为,也不会对外部产生影响。
function todoApp(state = { title: 'todoApp', todos: [] }, action) { switch (action.type) { case ADD_TODO: return Object.assign({}, state, { todos: [ ...state.todos, { data: action.data, completed: false } ] }) default: return state } }
todoApp
接收旧的 state
和 action
,并返回新的 state
。注意这里咱们是将 state
拷贝一份,再添加咱们改动的值去覆盖原来的数据,从新组合成新的 state
返回,而不是直接修改 state
。
这是由于若是你直接去改变 state
里对象的属性,那么就须要去比较新旧两个 state
的区别,而比较两个 Javascript
对象全部的属性是否相同就须要对它们进行深比较。可是在真实的应用中 js
的对象都很大,进行深比较的代价十分昂贵。而若是你返回的是一个全新的对象,就只须要比较新旧两个对象的存储地址是否相同就能够了。Redux
就是这么作的,若是你在 reducer
内部直接修改旧的 state
对象的属性值,那么新的 state
和旧的 state
将都指向同一个存储地址,Redux
会认为没有任何改变。
咱们能够有多个 reducer
,每一个 reducer
只负责管理全局 state
中它负责的那部分,每一个 reducer
的 state
参数能够都不一样,分别对应它管理的那部分 state
数据。而后经过 combineReducers
组成根 reducer
用来建立一个 store
。
import { combineReducers } from 'redux'; const todosReducer = (state = [], action) => { // do something } const titleReducer = (state = '', action) => { // do something } const reducer = combineReducers({ todos: todosReducer, title: titleReducer }); // 等价于 function reducer(state = {}, action) { return { todos: todosReducer(state.todos, action), title: titleReducer(state.title, action) } }
combineReducers
这个函数会调用你的定义的 reducer
,每一个 reducer
根据它们的 key(todos, title)
来筛选出 state
中的一部分数据处理并返回一份副本,根 reducer
会把这些副本组合起来造成一个新的大对象。最后根 reducer
将这个大对象传回给 store
,store
再将它设为最终的状态。
redux
一些基本概念咱们都清楚了,咱们来总结一下,Redux
为咱们所作的事情:
state
的地方actions
经过纯函数修改应用程序共享 state
的机制state
更新的机制严格的单向数据流是 Redux
架构的设计核心。
咱们只要清楚了 redux
中的数据流动的过程就明白 redux
整个工做流程了,咱们从产生一个 action
的切入点来分析数据是怎样流动的。
action
,这个 action
多是经过 actionCreator
返回的。store
接受这这个 action
以后,将当前的 state
和 action
一块儿传递给根 reducer
。reducer
将 state
分配给子 reducer
进行处理,子 reducer
返回修改后的副本给根 reducer
,根 reducer
整合子 reducer
返回的副本生成一个新的 state
副本返回给 store
。store
根据新的 state
触发视图层的渲染。action
...更多精彩内容,欢迎关注微信公众号~