前言
作为前端状态管理器,这个比较跨时代的工具库redux有很多实现和思想值得我们思考。在深入源码之前,我们可以相关注下一些常见问题,这样带着问题去看实现,也能更加清晰的了解。
常见问题
大概看了下主要有这么几个:
- redux三大原则
这个可以直接参考官方文档 - redux 的优缺点。 关于优缺点,太主观了大家见仁见智。
- redux中间件相关,洋葱模型是什么,常见中间件。
背景
有关acton,reducer相关的部分可以看我前面的文章。我们主要关注针对store和中间件相关的部分来解读。
store的创建
作为维护和管理数据的部分,store在redux中的作用十分重要。在action发出指令,reduxer进行数据更新之后,监听数据变化和同步数据更新的操作都要借助store来实现。
createStore 输入和输出
首先看下createStore的使用,即常见的就是接受经过combineReducers处理之后的reducer和初始的state
1 | import reducer from './reducers' |
此外还可以接受第三个参数enhancer(增强器,一般就是applyMiddleware)
1 |
|
按照一般的执行顺序,我们先看下对于参数的处理(平时大家也是一样,一个函数,执行之前尽量判断入参是否符合预期,避免直接处理造成的错误)
入参处理
对于三个参数,后两个是非必填的,但如果第二个参数是function,reduxe认为其实encher,不然初始状态是个函数不符合redux的预期,只能这样处理了。
1 | // 如果第二个参数为func,redux认为忽略了初始state,而是 |
dispatch的实现
dispatch的作用就是根据action,执行对应reducer以更新state。并执行监听队列。
下面就来看dispatch的用法和实现。
常见使用:1
2// redux要求 参数必须为纯对象
dispatch({ type: ActionTypes.INIT })
那么对于纯对象,redux做了些什么呢
1 | /** |
createStore初始化完成之后会执行dispatch({ type: ActionTypes.INIT }),此时执行初始化操作。
我们要关注下currentState的计算,
将currentState,action传给reducer处理,然后更新currentState。
针对初始化来说currentState其实就是initState:
1 | // 初始化状态 |
getSate实现
getState就是获得store的state。这个比较简单。当结合react-redux使用时,其会帮我们进行操作。我们就不用自行调用这个方法了,所以不要疑惑从哪里获取的state。
1 | /** |
subscribe
subscribe是比较重要的一个方法,用来供我们监听状态的变化,以执行相关操作。
例如react-redux中的handleChange 会对是否pure组件及state进行对比,以提升渲染效率。
示例:
1 | this.unsubscribe = this.store.subscribe(this.handleChange.bind(this)) |
实现:
返回的是一个函数,可以进行unsubscribe操作。
1 | /** |
replaceReducer
这个开发比较少用,用于热更新
1 | // 用于reducer的热替换,开发中一般不会直接使用 |
到这里createStore已经解析完成了,大家应该了解该方法到底做了些什么操作吧。
简单概括一下就是:接收reducer和initState,返回一个store 对象。该对象提供了监听、分发等功能,以实现数据的更新。
实际使用中的问题
经过上面的解读之后,对于redux的常规应用应该有所了解了。不过实际使用中可能会遇到些问题。
例如action要求是纯对象,而我们获取数据一般是异步的,这就需要借助redux-thunk这个中间件了。
actionCreater返回一个函数。如下:
1 | export function func1() { |
在了解如何实现之前,需要先看下redux中间件的原理。
因为reducer更多的关注的是数据的操作,对于一些公共的方法,需要抽离出来,不过这些方法在何时使用呢,redux为我们提供了中间件来满足需求。
redux中间件原理
redux 借鉴了 Koa里 middleware 的思想,即鼎鼎大名的洋葱模型。
不过这里请求对应的是dispatch的过程。
每次dispatch的过程中,都要依次将中间件执行一遍。
遇到阻塞或者操作完成,执行下个中间件,直到执行完成,以便我们事先日志,监控、异常上报等功能。
那么redux 又是如何支持中间件的呢。这就离不开applyMiddleware了。
这里前面的
applyMiddleware实现思路
实现思想比较简单,通过科里化和compose,为符合规范的中间件分配访问dispatch和store的途径,以便在不同阶段来自定义数据更新。
例如异步操作,返回的不是对象,那么就执行返回的函数,然后调用下一个中间件。等异步请求结束,再次dispatch 对应的action。
1 | export default function applyMiddleware(...middlewares) { |
这样执行之后返回的,对象就是增强之后的store了。
compose的实现
redux中compose是柯里化函数的一个示例,目的是将函数串联起来。
1 | /** |
结合redux-thunk示例
redux-thunk源码,实现也很优雅,对于返回的function,将dispatch等参数传递进去,然后执行,等待回调异步完成再dispatch。对于正常对象则进行下一步。
1 | function createThunkMiddleware(extraArgument) { |
那么实际使用时,在createStore时加入该中间件即可:
1 | import { createStore, applyMiddleware } from 'redux' |
那么到这里对于redux的中间件 也就是问题2,我想大家也比较清楚了。
对于常见中间件可以参考
结束语
参考文章
加上重读redux源码一和带着问题看 react-redux 源码实现总算将redux及react-redux重读了一遍。可能有人会说道这些源码,看完也会忘,有这个必要吗。我感觉分情况来看,如果我们只是使用,那么看官方文档就可以了,当遇到某些疑问好像找不到贴切解释的时候,不放一看。
此外也是学习大佬们的设计思路和实现方式,有的放矢才能开卷有益。