日益忙碌的一週又過去了,是時候開始每週一次的總結覆盤了,今天筆者就來剖析一下github中star數15.1k的開源項目redux-thunk。javascript
做爲一名React方向的前端工程師,不論是被面試仍是面試別人,大部分都會提及redux-thunk的實現原理,由於它很是經典且有用,並且代碼量少的感人,只有短短12行代碼,卻能解決React開發中同一個函數支持多dispatch和異步action的問題(雖然這徹底依賴於redux的中間件機制(Middleware))。css
接下來筆者將從:前端
-
Redux的工做機制vue
-
中間件實現原理java
-
redux-thunk源碼實現node
這三個方面來帶你們完全掌握redux-thunk源碼,從而對redux有更深刻的瞭解和應用。若是你們對react-redux-redux-thunk實戰感興趣的,讀完以後能夠移步筆者的《完全掌握redux》之開發一個任務管理平臺react
正文
在解讀Redux-thunk源碼以前咱們須要先掌握redux的基本工做機制和中間件實現原理,這樣才能更好的理解源碼背後的奧義。長話短說咱們先來看看redux的幾個核心api及其做用:webpack
redux解決的真正問題是React組件間的狀態共享和狀態管理問題,經過以上的6個核心api咱們便能管理複雜的狀態,並能監聽和追溯狀態的改動。機制筆者總結以下:ios
redux工做機理基本瞭解以後,咱們先看看一個實際的例子:css3
import actionType from './actionType' class Actions { static start() { return { type: actionType.CREATE_TODO_DOING } } static ok(data, cb) { cb && 'function' === typeof cb && cb(data); return { type: actionType.CREATE_TODO_SUCCESS, payload: data } } static fail(data, cb) { cb && 'function' === typeof cb && cb(data); return { type: actionType.CREATE_TODO_FAILURE, payload: data } } }
以上代碼咱們能夠發現咱們用了一個統一的createAction來建立action,在調用時只須要執行Actions.start()便可,咱們也知道action返回的是一個標準的對象,但咱們能夠在return以前作一些side effect。這裏咱們並不能在action中處理異步邏輯,這也是redux-thunk的價值之一,即解決異步調用action。
到這一步咱們仍然不能直接進入redux-thunk的源碼分析,由於咱們仍是不清楚如何解決上述步驟,由於咱們尚未了解redux的中間件機制。
redux中間件機制
說到中間件(middleware),使用過nodejs的人可能會很熟悉,好比說知名的koa中間件,express中間件等,其實中間件筆者的理解是在某個執行流中的某個環節作一些額外的處理的模塊。實現中間件的機制也很簡單, 就是在框架核心執行流中去遍歷外部傳入的中間件,並依次執行便可,咱們先來看看redux中如何使用中間件的:
import { createStore, applyMiddleware } from 'redux'; import reducers from './reducers'; const middlewares = applyMiddleware(middleware1, middleware2); const store = createStore(reducers, middlewares);
因此說redux-thunk是被傳入applyMiddleware方法中做爲參數使用的,不難猜到applyMiddleware方法中必定有遍歷執行參數的邏輯,咱們來看看applyMiddleware的核心源碼:
export default function applyMiddleware(...middlewares) { return function enhancer(createStore) { return function enhancedCreateStore(...args) { const store = createStore(...args) let dispatch = () => { thrownewError( '此處省略n個字...') } const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } const chain = middlewares.map( function(middleware) { return middleware(middlewareAPI) }) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch, } } } }
由上面的源碼可知,在chain這段代碼裏咱們發現其存儲的是applyMiddleware方法參數傳入getState,dispatch後的調用結果。接下來在dispatch這段代碼中出現了compose函數, 熟悉函數式編程的朋友不難猜到其內部確定是實現批處理chain的函數,並將store.dispatch泵送至其內部。上面源碼分析後咱們知道每一次執行dispatch時,都會先通過middleware的「洗禮」。
咱們再來看看compose函數的內部實現:
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[ 0] } return funcs.reduce( function(a, b) { return function (...args) { return a(b(...args)) } }) }
由上面代碼能夠看出compose最終返回的是一個函數,若是參數大於一時,咱們採用reduce將上一個函數返回的結果傳給下一個函數參數,以此來實現之間的參數共享和傳遞,很是經典的設計。
在掌握了redux中間件實現原理以後, 咱們再來看redux-thunk源碼就很是容易理解了。
redux-thunk源碼分析
咱們先看看這個github中star數15.1k的源碼長啥子:
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;
沒錯, 這就是redux-thunk的所有源碼了,是否是很nice~。在上面的介紹中咱們瞭解到redux中間件機制使得咱們能夠在中間件中拿到必備的dispatch, getState,而且在執行以前已經調用了兩層middleware,此時咱們能夠解剖一下createThunkMiddleware,在第一次調用createThunkMiddleware是在chain階段,即上面源碼分析的:
因此這裏的next也就是第二次調用時的store.dispatch, 爲了實現同一函數內能執行屢次dispatch,咱們會判斷若是action爲函數,則執行action自己並把必要參數傳遞給它,不然則直接觸發dispatch,這樣咱們就實現了支持action爲函數而且支持異步多dispatch的功能了,讀到這仍是很是感嘆其設計的優雅和簡潔,不經讓筆者感嘆:學好函數式,走遍天下都不怕!
最後筆者準備了一個基於React+redux+redux-thunk的實戰項目,github地址:
https://github.com/MrXujiang/redux_OA
感興趣的能夠學習參考一下。
最後
若是想學習更多H5遊戲, webpack,node,gulp,css3,javascript,nodeJS,canvas數據可視化等前端知識和實戰,歡迎在公號《趣談前端》加入咱們的技術羣一塊兒學習討論,共同探索前端的邊界。
更多推薦
本文分享自微信公衆號 - 趣談前端(beautifulFront)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。