redux-thunk是什么东西
在此之前先回顾下redux的工作流程:action-> dispatcher -> reducer -> store tree changed -> relative components re-render -> UI changed。这整个过程都是同步的,只要action被dispatch到reducer,对应state发生变化,UI就立即更新。那需要异步访问服务器的场景该怎么办呢?比如我的store上的某些数据需要从服务器上取,取完之后才能dispatch对应的action。所以我们需要在action对象disptach到reducer之前就应该完成必要的异步过程。
Redux提供了thunk这种解决方法,那么thunk又是什么东西呢?看下redux-thunk的github上的解释:
A thunk is a function that wraps an expression to delay its evaluation.
thunk是一个包裹着一个表达式以延迟其求值的函数。
同时举了个例子:
// 表达式1 + 2会被立即计算出来
// x === 3
let x = 1 + 2;
// 包裹在foo这个函数里作为返回值时
// 表达式1 + 2只在函数foo执行的时候才会被计算
// foo就是一个thunk,它延迟了1 + 2的执行
let foo = () => 1 + 2;
redux-thunk也是这样一个函数,对于redux-thunk而言它所谓延迟求值的是指的什么呢?我们上面说到redux里action创建出来到被dispacth到reducer的过程是同步的,我们要实现异步的action就必须延迟action对象的dispatch,这就是对redux-thunk而言所谓的延迟求值。
再结合github上的介绍:
Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.
Redux Thunk中间件允许我们写一个返回function而非action对象的action creators。Redux Thunk可以用来延迟dispatch一个action或者只在某些特定场景下才dispatch。内部函数接受store的dispatch方法和getState方法作为参数。
这里中间件是什么,还是看下文档
It provides a third-party extension point between dispatching an action, and the moment it reaches the reducer. People use Redux middleware for logging, crash reporting, talking to an asynchronous API, routing, and more.
试着翻译一下:
Redux的中间件在dispatch action和action对象到达reducer之前提供了一个第三方扩展点(什么鬼?原谅我只会google翻译)。我们可以用redux中间件来做一些诸如日志记录、创建崩溃报告、调用异步接口或者路由之类的事。
好吧,我感性的理解下:Redux里的中间件是这样的一个东西,这个东西加在dispatch action和action到达reducer这个过程之间,加了有了它之后呢,redux的功能就比不加它强了,可以dispatch函数形式的action了,当然,加的功能都应该定义在这个函数里。比如redux-thunk这个中间件加强的功能就是,在dispatch真正传给reducer的action对象之前允许你去做一些异步操作,异步操作结束后再把真正的action对象dispatch出去;再比如上面说的有着日志记录功能的中间件,这个中间件在dispatch action对象前后打印一些日志信息。
然后借用《深入浅出React和Redux》P183里对中间件特点的总结:
- 中间件是独立的函数(记住这点很重要,它不是什么很稀奇古怪的东西,就是一个函数,只不过这个函数能处理函数类型的action)
- 中间件可以组合使用(这个应该很容易接受,不可能只用一个中间件让它去做所有事情)
- 中间件有一个统一的接口(没有统一接口的话组合使用的难度应该会比较大)
好了,这下可以说清除redux-thunk是个什么东西了:首先redux-thunk(前文用redux-thunk的地方指的应高都是Redux Thunk)指的是一个npm包,这个npm包提供了Redux Thunk这个中间件,这个中间件允许我们在dispatch action对象到reducer之前做一些异步操作。
搞清楚它是什么,有什么用之后,我们需要知道它怎么起作用的,这个“它”指的是Redux Thunk,就不再把它和redux-thunk混为一谈了
Redux Thunk原理是什么
直接上源码(哈哈,每论及源码的时候总感觉好霸气的样子):
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next =>action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
看它的默认导出,const thunk = createThunkMiddleware()
thunk是一个函数,这个函数为createThunkMiddleware()
的返回值,即第二行return后面的内容。
同时由thunk.withExtraArgument = createThunkMiddleware
可知thunk还有一个方法,这个方法指向最外层的那个函数,用于传入额外参数的情况,目前我用到的都是不用传参的情况,那这部分暂时不去理会。
继续说thunk这个函数,如前所述,它以dispatch和getStore为参数,返回值依然是一个函数,记为函数1,函数1以next作为参数,由后面next(action)
可知这个next依然是一个函数,函数1的返回值还是一个函数,记为函数2,函数2才是实际起作用的关键,前面的一堆操作暂时可以理解为花架子(当然我知道实际上并不是,涉及到函数式编程的思想),我们具体看函数2:
action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
处理逻辑很简单:如果传过来的action类型是函数就传入dispatch, getState参数给这个action函数执行之并返回,否则执行next(action),next()函数的作用是把action对象传给下一个action处理者,或为一个中间件或为最终的reducer。因此,知道它的处理机制后我们通过Redux Thunk来执行异步操作(即定义需要异步操作的的action函数)的套路就有了:
export const sampleAsyncAction = () => {
return (dispatch, getState) => {
//执行异步操作,然后相机dispatch新的action对象
}
}
所以一个添加了Redux Thunk中间件的action流程如下:
关于redux-thunk就说到这了。
另外说下涉及异步操作下的store里的state tree,
此时state tree里的数据可以分为两部分,一为涉及到异步操作(即需要访问服务器进行存取)的部分;一为只涉及同步更新,并不用存储于服务器的部分。这里只用说前者,目前我的理解是,对于那些需要从服务器上读取的数据,我们是不是只需要在依赖这些数据的组件挂载时访问服务器进行获取数据,同时将获取到的数据同步到state tree,后面的话,只要这部分数据不需要改动那么我们便不用再去访问服务器来获取了,直接用state tree上的数据,因为二者的数据是同步的。对于需要改动的数据,我们访问服务器对其进行写操作成功之后也立即将state tree同步更新。那么就是说除了组件挂载时需要访问服务器获得数据之外,如果单单只是为了获取数据而不用进行写操作的话,那我们只用从state tree上获取数据就可以了。是不是这样?
成了?
最后
关于函数式编程:因为想之前有想要去了解redux的中间件到底是如何起作用的,所以去看了compose, applyMiddleare相关部分的源码,虽然内容不多(哪是不多,相比印象中但凡说源码动不动几百上千行的来比简直少到天上去了,貌似这两个加起来就几十行),但是还是看不下去,转头去了解了下函数是编程相关的东西,也是点云里雾里就暂时搁置。对函数式编程的感觉倒是和知乎有个相关问题下面上轮子哥vczh的回答里的有点像:函数式编程在使用的时候的特点就是,你已经再也不知道数据是从哪里来了,每一个函数都是为了用小函数组织成更大的函数,函数的参数也是函数,函数返回的也是函数,最后得到一个超级牛逼的函数,就等着别人用他来写一个main函数把数据灌进去了。
最后,真的佩服阮一峰前辈这样的奉献者,网上找前端相关知识点的教程几乎都能搜到他,前人栽树后人乘凉,希望有一天自己也能成为这样的前人,能栽出些可以让后人乘凉的树,同时也能感染后人去栽树。