原文地址在个人博客, 转载请注明出处,谢谢!javascript
本文介绍了我对 Redux 状态管理的思想、原理、架构方法的认识和思考以及配合redux-saga处理异步操做的实践java
You know, React 只是属于MV*架构模式的 view 层,是一种状态机,只使用 React 难以控制大型、复杂的应用,它须要一些框架来帮助管理状态,所以如何有效、简单、易于测试地管理这个状态机是各类架构框架感兴趣的。Facebook 早就意识到这个问题并提出了 Flux 架构,比较复杂; 后来出现了 Redux、Mobx 等。MobX 能够处理简单数据流的场景,能够实现精确更新; Redux 是从 Flux 和其余框架借鉴了一些思想, 它比 Flux 简单、易于理解、用于处理复杂数据流,并具备很强的扩展性,社区诞生了像redux-thunk、redux-promise、redux-saga等中间件用于方便地处理异步操做。最近也在项目中使用了 Redux 及其中间件 redux-saga 来管理状态和处理异步操做,这篇文章就来谈谈我对它们的思考和实践。node
先来谈谈背景(需求)react
我以为理解Redux的思想,谈谈MV*架构模式的思路也许会有帮助。redux
MV*架构模式,它们的核心都是职责分离、解耦,不一样的层次作不一样的事,能让一个复杂、混乱的应用变得思路清晰,代码能够复用,而且易于测试,有利于分工合做,构建更大、更复杂的应用。promise
一个应用要包括哪些功能?表现(view)、处理数据的逻辑(model)以及数据映射到表现层的逻辑(presenter or controller),数据在这三层之间流通(MVVM模式经过数据双向绑定实现view和model同步)。浏览器
React 根据state来render,它只是个状态机,并无解决管理状态的问题。咱们在单纯的使用React来写组件的时候,常常会遇到组件间通讯和管理组件state的问题,前者经常使用的解决办法就是把数据提到父组件共享;后者管理state简单的状况还行,一复杂就很麻烦且容易出错,再遇到一些须要异步处理的操做,想一想就头皮发麻。react-router
当你开发中遇到一些反人类的操做时,试着去想如何改变一下思路让它变得更简单,别耐着性子安慰本身开发就是这样 :)架构
解决方案框架
Redux 正是用来解决大型React应用所面临的状态管理、数据流通、异步处理、测试、团队合做等问题:
Redux 用单一的object tree来表示整个应用的state,这个表示state的对象树被放在惟一的store 中,state至关于store的快照;全部组件都会经过API拿到这个state,各取所需;
Redux 把页面上用户的操做或者浏览器的行为(如路由的变化)定义为一个要更新state的action
,这个action
是一个普通对象,它包含了要执行动做的类别和传递到state的数据(若是有的话),它只代表要更改state的意图,至关于一个信号,并不能直接修改state,Redux会集中处理这些信号,这个action
由你来决定什么时候发起;
定义好信号,你还须要根据不一样的信号定义不一样的逻辑函数(reducer
s)来更新state。
经过这张图来整理一下:
咳咳...好比用户点击的一个按钮,你在按钮上绑定的回调函数调用了一个(多个)action
creator,action
creator就返回了一个更新state局部数据的action
,store会根据这个(多个)action
找到对应的reducers(reducer须要作拆分),按照action
发起的顺序依次执行来更新state,每一个reducer只负责更新本身关心那部分,根 reducer 把多个子 reducer 输出合并成一个单一的 state 树,生成一个新的state保存在store中,store中的state能够经过相应API传递到子组件。
这就是整个数据流。
那Redux如何处理异步操做?
Redux借鉴了中间件思想,利用可扩展的中间件来改造dispatch函数。好比redux-thunk让dispatch不只仅能够接收action
,还能够接受函数做为参数,你能够在这个函数里完成异步操做。再如redux-saga更强大、也更复杂,在后面会讲到。
对于React技术栈,Redux实现了react-redux库来让Redux管理React应用(其余框架也有相应的库),里面集成了一些有用的函数来把一些明确的流程自动化,如createStore
用于建立惟一store,能够把根reducer传进createStore
使store自动调用对应reducer,能够扩展中间件;提供<Provider store>
组件和connect高阶组件用来包裹render component并传递state,connect还能自动dispatch,让你只要调用action
creator就能dispatch;提供combineReducers来组合分割的reducers等。
知道这些特性,就能够配合react-router构建大型应用了:
总的思路就是:利用react-router 把应用分割为各个页面,reducer、action
creator也跟随页面分割而分割。每一个路由对应的页面下都有components和containers,分别存放functional components 和class components,前者用来渲染,后者当作containers被connect包裹,containers包裹components;containers从connect获得state并映射须要的数据到子组件的props,子组件再向下传递。
具体如何构建React + Redux + react-router,我在另外一篇博客里讲了。
使用这种架构,开发大型应用变得驾轻就熟。
可是当我深刻项目开发的时候,也逐渐发现了一些问题:
action
creator都定义在页面层次上,让子组件调用必须一层一层的传递,很麻烦且很是容易出错,也很难调试;reselect
)总之,redux能够胜任复杂数据流的应用,可是也比较难,前期架构比较麻烦,适合有经验的人。
Redux 倡导action
和reducer尽量"纯净",没有什么“反作用”。但是像一些异步操做好比获取数据是必须的,在哪处理这些反作用呢?redux 把这些"不纯净的"任务交给了中间件,经过 向createStore
里应用中间件,在交由store处理action
以前就能够对其完成一些其余的操做:
而redux-saga 是Redux一个强大但并不复杂的用于异步处理的中间件。
它的思路是什么?相比其余redux异步中间件如redux-thunk、redux-promise有什么不一样?
先看名字来理解:saga,这个术语经常使用于CQRS架构,表明查询与责任分离。
没错,就是查询(dispatch)与责任(sagas)分离。saga提供了action
监听函数,只需在组件里dispatch 相应type的action
,就能够自动调用你定义好的对应这个action
的异步处理函数(sagas)来完成任务,保证了只在组件里dispatch action
来发起异步操做而不是redux-thunk、redux-promise的调用action
creators。
另一大特点就是redux-saga作到了异步代码以同步方式写,很是直观方便,怎么作到的呢?它是利用了ES6新魔法Generator迭代器,能够完美解决异步回调地狱,让你以同步方式写异步。saga正是利用Generator特性让其处理异步变得很是方便又容易理解。这是一个常见的请求后台数据的异步操做,感觉一下:
function *fetchNodeDetailByNodeId({ payload: { nodeId } }, { call, put }) {
try {
const { data, status }= yield call(fetchNodeDetailByNodeId, nodeId)
if (data && status.errmsg === 'success') {
yield put({
type: 'setStates',
payload: {
nodeDetailData: data,
},
});
} else {
message.info('开了个小差,再试一次吧..');
}
} catch (error) {
console.log(error);
}
},复制代码
call 和 put 是saga的API,至关于dispatch,可是并非真正执行dispatch,只是发送你指定的指令,交由saga中间件来执行这个指令。这样看来,这个saga函数就是一些指令的集合,称为effects,反作用,用来描述任务
为啥要描述指令而不直接调用呢?这样是由于易于测试,若是直接调用,你还得模拟调用的函数,详见redux-saga文档。
我以为redux-saga相比于其余中间件的优势:
action
的纯洁性,符合redux设计思想讲道理,任何redux异步操做均可以让saga这个中间件来完成,很是复杂的一样能够胜任,而且很容易理解(异步操做以同步方式写)和测试。再配合dva,能够减轻redux的复杂度同时完成更强大的功能。
这样以来,redux配合saga,就可让它们各司其职,整个思路也变得清晰起来:
redux 倡导action
和reducer要纯洁,那就让全部异步操做这些不纯洁的任务交给saga,reducer不用变,仍是纯函数;定义好对应action
的sagas专门用来处理异步操做,我只要在组件须要的地方里dispatch 纯action
就好了,符合redux设计思想。
使用redux来管理应用状态适用于复杂的应用,而复杂的应用会有复杂的异步处理,异步处理不要用redux的action
creator,它不是用来作这个的,也违背了redux设计思想,redux把这些任务交给了异步中间件,应该由它们来完成。使用redux saga是一个推荐的选择,它懂redux,也懂你须要什么。另外,既然你用到了saga,不妨试试dva架构,5分钟上手,值得一试。