中间件的用途
Redux中间件叫middleware,主要是在Redux发起一个Acttion还没到达Reducer之前或者Reducer执行之后做一些处理,例如记录日志、发起请求等,完成之后再调用真正的diapatch更新数据,用法如下:
import { createStore, combineReducers, applyMiddleware } from 'redux'
let todoApp = combineReducers(reducers)
let store = createStore(
todoApp,
// applyMiddleware() 告诉 createStore() 如何处理中间件
applyMiddleware(logger, crashReporter)
)
在createStore时传入applyMiddleware函数,而applyMiddleware参数正是中间件,这样在每次diapatch的时候会先执行中间件的逻辑。
实现原理
在了解中间件如何使用后,我们再来看一下它是如何实现的,首先我们来看下中间件的具体写法。
const logger = store => next => action => {
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
参数store与action我们都很熟悉,next实际上指的就是dispatch,这个中间件的作用是打印日志,在action发起之前和之后打印一些信息,执行next就是执行真正的dispatch去更新数据。我们再来看下applyMiddleware这个函数做了什么,为什么中间件要这么写。
// 警告:这只是一种“单纯”的实现方式!
// 这 *并不是* Redux 的 API.
function applyMiddleware(store, ...middlewares) {
let dispatch = store.dispatch
middlewares.forEach(middleware =>
dispatch = middleware(store)(dispatch)
)
return { ...store, dispatch }
}
这个并不是applyMiddleware真正写法,之所以这么写,只是为了便于理解applyMiddleware的实现原理。我们传入了很多中间件middlewares,然后再用数组去遍历它。middleware(store)(dispatch)这里就是调用了const logger = store => next => action => {}的store => next这一层,最返回的是一个函数。这里非常巧妙,执行下标为0的middleware传入store.dispatch,然后再把dispatch变为下标为0的middleware返回的函数,执行下标为1的的middleware,再把下标为0的middleware返回的函数diapatch作为参数传给下标为1的的middleware,以此类推下去,最终的效果就是嵌套了很多层。最后applyMiddleware返回的时候把store的dispatch变成了嵌套很多层的dispatch。
当我们毎执行一个dispatch时,它会先调用了嵌套很多层的dispatch,最后再执行真正的dispatch去更新数据,现在我们掌握了中间件为啥要那么写了。
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;
我们重点看action,我们一般dispatch(action),action一般为普通对象,这里却判断action的类型如果是一个方法,就执行它。Redux-thunk的作用显而易见了,将action改写为一个函数,这里一般为异步函数,可以返回一个promsie,结束后在调用普通的dispath去更新数据。Redux-thunk虽然解决了异步调用的问题,但是同时也违背了只能dispatch一个action的原则,并且它不够强大,现在解决异步调用一般都是用Redux-saga。