Open MrErHu opened 7 years ago
作者你好 我有个问题想求教一下
并没有直接使用dispatch:dispatch,而是使用了dispatch:(action) => dispatch(action),其目的是如果使用了dispatch:dispatch,那么在所有的Middleware中实际都引用的同一个dispatch(闭包),如果存在一个中间件修改了dispatch,就会导致后面一下一系列的问题,但是如果使用dispatch:(action) => dispatch(action)就可以避免这个问题。
每一个中间件得到的参数都是middlewareApi,所有的中间件的middlewareApi都会被修改 比如其中一个中间件是function(api){ api.dispatch=xxx }
@mjc224000 你说的是对的,最近自己回看之前写的文章发现发现自己之前很多地方理解不太到位,
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
};
采用这种方式反而才是让所有的中间件中的dispatch都引用的同一个引用。而这些中间值中得到的dispatch 恰好就是被包装过的dispatch:
dispatch = compose(...chain, store.dispatch);
所以这个的目的应该是让所有的middleware拿到的dispatch都是经过包装的dispatch,而不是原生的store.dispatch 温故而知新,看来自己要学习的还是有很多。
这段时间都在学习Redux,感觉对我来说初学难度很大,中文官方文档读了好多遍才大概有点入门的感觉,小小地总结一下,首先可以看一下Redux的基本流程:![](https://segmentfault.com/img/remote/1460000008322586?w=800&h=600)
从上面的图可以看出,简单来说,单一的
state
是存储在store
中,当要对state
进行更新的时候,首先要发起一个action
(通过dispatch
函数),action
的作用就是相当于一个消息通知,用来描述发生了什么(比如:增加一个Todo),然后reducer
会根据action
来进行对state
更新,这样就可以根据新的state
去渲染View。 当然上面仅仅是发生同步Action的情况下,如果是Action是异步的(例如从服务器获取数据),那么情况就有所不同了,必须要借助Redux的中间件Middleware。根据官方的解释,Redux中间件在发起一个
action
至action
到达reducer
的之间,提供了一个第三方的扩展。本质上通过插件的形式,将原本的action
->redux
的流程改变为action
->middleware1
->middleware2
-> ... ->reducer
,通过改变数据流,从而实现例如异步Action、日志输入的功能。 首先我们举例不使用中间件Middleware创建store:那么使用中间件的情况下(以
redux-logger
举例),创建过程如下:那么我们不经要问了,为什么采用了上面的代码就可以实现打印日志的中间件呢? 首先给出applyMiddleware的源码(Redux1.0.1版本):
上面的代码虽然只有不到20行,但看懂确实是不太容易,实际上包含了函数式编程各种技术,首先最明显的使用到了柯里化(Currying),在我理解中柯里化(Currying)实际就是将多参数函数转化为单参数函数并延迟执行函数,例如:
关于柯里化(Currying)更详细的介绍可以看我之前的一篇文章从一道面试题谈谈函数柯里化(Currying)。 首先我们看applyMiddleware的总体结构:
哈哈,典型的柯里化(Currying),其中
(...middlewares)
用到了ES6中的新特性,用于将任意个中间件参数转化为中间件数组,因此很容易看出来在该函数的调用方法就是:其中applyMiddleware形参和实参的对应关系是:
middlewares
[middleware1,middleware2]
createStore
createStore
reducer, preloadedState, enhancer
createStore
需要填入的参数再看函数体:
上面代码非常简单,首先得到store,并将之前的
store.dispatch
存储在变量dispatch
中,声明chain
,以及将middleware
需要的参数存储到变量middlewareAPI
中。接下来的函数就有点难度了,让我们一行一行来看。上面实际的含义就是将middleware数组每一个
middleware
执行middleware(middlewareAPI)
的返回值保存的chain数组中。那么我们不经要问了,中间件函数到底是怎样的?我们提供一个精简版的createLogger函数:可见中间件
createLogger
也是典型的柯里化(Currying)函数。{getState}
使用了ES6的解构赋值,createLogger(middlewareAPI))
返回的(也就是数组chain
存储的是)函数的结构是:我们接着看我们的
applyMiddleware
函数这句是最精妙也是最有难度的地方,注意一下,这里的
...
操作符是数组展开,下面我们先给出Redux中复合函数compose
函数的实现(Redux1.0.1版本):首先先明确一下
reduceRight
(我用过的次数区区可数,所以介绍一下reduce
和reduceRight
)reduce
和reduceRight
的区别就是从左到右和从右到左的区别。所以如果我们调用compose([func1,func2],store.dispatch)
的话,实际返回的函数是:胜利在望,看最后一句:
这里其实是ES7的用法,相当于ES6中的:
或者是Underscore.js中的:
懂了吧,就是新创建的一个对象,将store中的所有可枚举属性复制进去(浅复制),并用当前的
dispatch
覆盖store中的dispatch
属性。所以中的
store
中的dispatch
属性已经不是之前的Redux原生的dispatch
而是类似于func1(func2(store.dispatch))
这种形式的函数了,但是我们不禁又要问了,那么中间件Midddleware又是怎么做的呢,我们看一下之前我们提供的建议的打印日志的函数:假设一下,我们现在使用两个中间件,
createLogger
和createMiddleware
,其中createMiddleware
的函数为调用形式为:
如果调用了
store.dispatch(action)
,chain
中的两个函数分别是createLogger
和createMiddleware
中的部分。我们姑且命名一下
chain
中关于createLogger
的函数叫做func1
,关于createMiddleware
的函数叫做func2。那么现在调用store.dispatch(action)
,实际就调用了(注意顺序)上面的函数大家注意之前执行次序,首先
func2(store.dispatch
再是func1(args)(action)
。对于func1获得的next的实参是参数是:那么实际上
func1(...)(action)
执行的时候,也就是的时候,getState调用的闭包MiddlewareAPI中的Redux的getState函数,调用next(action)的时候,会回调
createMiddleware
函数,然后createMiddleware
中next
函数会回调真正的store.dispatch(action)。因此我们可以看出来实际的调用顺序是和传入中间件顺序相反的,例如:实际的执行是次序是
store.dispatch
->Middleware3
->Middleware2
->Middleware1
。 不知道大家有没有注意到一点,并没有直接使用
dispatch:dispatch
,而是使用了dispatch:(action) => dispatch(action)
,其目的是如果使用了dispatch:dispatch
,那么在所有的Middleware中实际都引用的同一个dispatch
(闭包),如果存在一个中间件修改了dispatch
,就会导致后面一下一系列的问题,但是如果使用dispatch:(action) => dispatch(action)
就可以避免这个问题。 接下来我们看看异步的action如何实现,我们先演示一个异步action creater函数:上面的代码用来从Github API中拉取名为username的用户信息,可见首先
fetchUserInfo
函数会dispatch
一个表示开始拉取的action
,然后使用fetch
函数访问Github的API,并返回一个Promise,等到获取到数据的时候,dispatch
一个收到数据的action
,最后dispatch
一个拉取结束的action
。因为普通的action
都是一个纯JavaScript Object对象,但是异步的Action却返回的是一个function,这是我们就要使用的一个中间件:redux-thunk。 我们给出一个类似redux-thunk的实现:这个和你之前看到的中间件很类似。如果得到的action是个函数,就用dispatch和getState当作参数来调用它,否则就直接分派给store。从而实现异步的Action。 Redux入门学习,如果有写的不对的地方,希望大家指正,欢迎大家围观我的博客: MrErHu SegmentFault