本文是关于 redux(3.7.2)源代码的一些浅读
在redux源码目录中 ,可以看到以下文件目录:
1 | |-- utils/ |
与文件对应的,主要也是介绍 createStore compose combineReducers bindActionCreators applyMiddleware这几个函数。
1. compose
先来看看compose函数,这个比较简单。
1 | export default function compose() { |
其中主要就是对 reduce 的理解了。reduce()方法使用指定的函数将数组元素进行组合(从左到右),生成单个值。具体可以查看Array.prototype.reduce() | MDN
compose的功能为从右到左,组合参数(函数)。传递给compose方法的参数是函数类型的,
compose(f, g, h) 相当于 (…args) => f(g(h(…args)))
2. applyMiddleware 与 中间件链
故名思义这个函数就是应用中间件,一般使用方式为:
1 | //thunk代表react-thunk中间件 |
2.1 中间件及react-thunk
在看applyMiddleware函数前,先来简单看一下中间件,redux中间件在发起action到达reducer之间扩展了一些功能。一般用于记录日志(reudx-logger)和增加异步调用(redux-thunk , redux-saga)等。这些中间件一般按照一定的格式书写,比如react-thunk2.2.0的源码:
1 | //react-thunk2.2.0的代码,很简单 只有十几行…… |
可以看到react-thunk中间件是一个多层的高阶函数,格式大致为:
1 | ({dispatch,getState}) => next => action => {return next(action)} |
中间件的格式都要遵循这样相似的格式,这是由applyMiddleware函数决定的。接下来看下applyMiddleware的源码:
2.2 applyMiddleware源码
1 | //applyMiddleware源码 |
在_dispatch = compose.apply(undefined, chain)(store.dispatch);
组合后,中间件的next就是store.dispatch一路经过上一个中间件封装后的变种dispatch。
2.3 中间件链的执行顺序
光看代码可能对于中间件链的执行顺序的理解还是优点蒙,这里来做个实践加深一下理解。
一、 先按上文说的中间件格式
({dispatch,getState}) => next => action => {return next(action)}
写三个中间件:
1 |
|
二、 将它们和redux-thunk 和 redux-logger一起加入中间件链:
1 | const middlewares = [ |
运行你的代码后在chrome控制台看到如下信息:
1 | middleware3 next层 next: ƒ (l){if("function"==typeof … |
这也验证了上文对 _dispatch = compose.apply(undefined, chain)(store.dispatch);
的理解。
这里的中间件链是由 [m1,thunk,m2,m3,logger]
组成,在compose
之后变成了 m1(thunk(m2(m3(logger))))
,
所以
- m3的next参数是logger的action层函数(参数为action那层函数)
- m2的next参数是m3的action层函数
- m1的next参数是thunk的action层函数
三、 执行一下 dispatch(action) :
这时候分两种情况:
1、当action类型为对象时,在chrome控制台看到如下信息:
1 | 发起 dispatch(action) action类型为对象 |
粗粗一看好像顺序不对啊,不该先执行middleware3的逻辑嘛?其实内层(位置靠后的中间件)只是返回了一个function,并没有执行其中的逻辑,不断由外层的中间件包裹形成了一个‘洋葱模型’。由外向内穿心而过,再由内向外完成流程。
这样子就很明确了,中间件的action层执行顺序为先加入中间件链的先执行!,更准确的说中间件中先执行从外层向内层中 next(action)
之前的逻辑,然后执行从内层向外层中 next(action)
之后的逻辑。
2、当action类型为函数时,在chrome控制台看到如下信息:
1 | 发起 dispatch(action) action类型为函数 |
可以看到只执行了 middleware1 和 thunk 两个中间件!这是因为thunk没有执行 next(action)
中断了中间件链! 当中间件没有执行next(action)时会导致中间件链中断,这是因为dispatch没有传递下去,所以中间件还可以捣乱咯~
3. bindActionCreators
bindActionCreators的功能是为action creaters包装上dispatch,使其调用action时自动dispatch对应的action。
在bindActionCreators.js中只有两个函数 bindActionCreator
和 bindActionCreators
。
1 | //这个函数的主要作用就是返回一个函数,当我们调用返回的这个函数的时候,就会自动的dispatch对应的action |
4. combineReducers
Reducer只是一些纯函数,它接收之前的state和action,并返回新的state。当应用变大,我们可以拆分多个小的reducers,分别独立的操作state tree的不同部分。而combineReducers就是将多个不同的reducer合并成一个最终的reducer,用于赋值给createStore函数。
1 | //combineReducers的代码比较简单,在省略一些错误判断的代码后: |
5. createStore
现在来看下最重要的createStore函数:
1 | /* |
上面是createStore的参数,其中enhancer指的是store的增强器(对store API进行改造)。
- applyMiddleware在前面我们已经见过了,applyMiddleware是对store的dispatch函数的修改。
- 时间旅行则是指redux可以回到任意以前的状态。
- 这是因为Redux使用简单的对象来表示state状态,并使用纯函数计算下一个状态。这意味着如果给定一个特定state状态和一个特定action操作,那么下一个状态将始终完全相同。这种特性可以让我们将所有修改过的状态保存在一个状态历史对象中,通过指定恢复到状态历史对象中从前的状态,来完成时间旅行。
- 比较容易可以想到的应用就是一些撤销/重做操作。
- 持久化持久化这个概念肯定大家都熟悉,也有人做出了实现:redux-persist
介绍完enhancer,来接着看代码逻辑:
1 | export default function createStore(reducer, preloadedState, enhancer) { |
以上就是我个人的简单见解,如果有什么错误之处,敬请指导讨论
参考资料: