瞭解了Redux原理以後,我很好奇Redux中間件是怎麼運做的,因而選了最經常使用的redux-thunk進行源碼分析。javascript
這次分析用的redux-thunk源碼版本是2.2.0,redux源碼版本是3.7.2。而且須要瞭解Redux原理java
redux中間件都是由redux的applyMiddleware()方法所掛載的redux
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主要作了如下事情:app
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行spa
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})=>{...}
這個函數。code
而後經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
傳遞給下一個中間件。ip
具體到redux-thunk中,它先檢查action
是不是函數,通常的action
都是plain object,若是是函數就應該是由thunk處理。若是不是,傳遞給next
,next
就是下一個中間件。
若是是函數,則調用這個函數並將dispatch, getState, extraArgument
傳入。這也就是爲何咱們須要將thunk action生成函數(注意action和action生成函數的區別)寫成() => (dispatch, getState) => {...}
,傳入redux-thunk的action
就是(dispatch, getState)=>{...}
這個函數