【源码解析】redux-thunk
了解了Redux原理之后,我很好奇Redux中间件是怎么运作的,于是选了最常用的redux-thunk进行源码分析。
此次分析用的redux-thunk源码版本是2.2.0,redux源码版本是3.7.2。并且需要了解Redux原理
redux中间件都是由redux的applyMiddleware()方法所挂载的
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { const store = createStore(reducer, preloadedState, enhancer) let dispatch = store.dispatch let chain = [] //暴露给中间件的API,所以redux-thunk可以使用形如return (dispatch, getState)=>{} const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } //将暴露的API给中间件,调用中间件函数,生成中间件返回值函数(为什么返回值是函数?不是函数下面的compose()就报错了) chain = middlewares.map(middleware => middleware(middlewareAPI)) //组合全部中间件的返回值函数, chain是中间件返回值函数们 //然后reduce起来的函数返回的也是函数,将store原来的dispatch传进去,dispatch函数也是接受一个action并返回一个action,作为中间件链的头部 dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
可以看到redux主要做了以下事情:
- 对中间件们使用
map
,将dispatch
和getState
传递进去 - 使用
compose
将中间件组合起来,最后传入原生的store.dispatch
而compose
函数则是简单的将中间件进行串联调用
//compose(funcA, funcB, funcC) 等于 (args)=>funcA(funcB(funcC(args))) export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }
最后我们回到redux-thunk
redux-thunk的代码很短,只有短短14行
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();
,也就是({dispatch, getState})=>{...}
这个函数。
然后经chain = middlewares.map(middleware => middleware(middlewareAPI))
传入dispatch
和getState
,chain
里面是next=>{...}
,进行compose
。
compose(a,b,c)
的返回值是函数(...args)=>a(b(c(...args)))
,这个函数经调用,传入参数store.dispatch
,之后action
会在中间件链上进行传递,只要保证每个中间件的参数是action
并且将action
传递给下一个中间件。
具体到redux-thunk中,它先检查action
是否是函数,一般的action
都是plain object,如果是函数就应该是由thunk处理。如果不是,传递给next
,next
就是下一个中间件。
如果是函数,则调用这个函数并将dispatch, getState, extraArgument
传入。这也就是为什么我们需要将thunk action生成函数(注意action和action生成函数的区别)写成() => (dispatch, getState) => {...}
,传入redux-thunk的action
就是(dispatch, getState)=>{...}
这个函数