前傳 中間件的由來 redux的操作的過程,用戶操作的時候,我們通過dispatch分發一個action,純函數reducer檢測到該操作,並根據action的type屬性,進行相應的運算,返回state,然後更新view。 但是一個很重要的問題,reducer對於action會立即進行運算,並返回 ...
前傳 中間件的由來
redux的操作的過程,用戶操作的時候,我們通過dispatch分發一個action,純函數reducer檢測到該操作,並根據action的type屬性,進行相應的運算,返回state,然後更新view。
但是一個很重要的問題,reducer對於action會立即進行運算,並返回state,如果我們的操作是要獲取服務端的數據,需要調用介面類似的非同步操作呢?很明顯這樣操作不行。所以,middleware中間件誕生了,中間件就是處理reducer處理不了的問題,對reducer做一個補充,配合。
我們就以使用中間件來解決非同步問題為例來說,中間件顧名思義,就是作為一個流程的一個中間處理程式存在,它是放到一個流程的中的。問題是在處理一部問題,我們把他放哪的問題
a、action只是一個跑腿的,把操作的相關數據帶到reducer;
b、reducer只是個幹活的,純函數,計算一下嘛,返回新的數據,非同步操作它搞不了;
c、view,理論上這或者是顯示的,不會有太多的邏輯,中間件肯定不能放;
d、剩下的就是把action分發到reducer的dispatch了,我們可以在分發的時候做點手腳。
看看applyMiddleware
中間件知道放在哪了,我們看看實現中間件的重要api ----- applyMiddleware 的源碼
function applyMiddleware() { for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) { middlewares[_key] = arguments[_key]; } return function (createStore) { // 以下稱這個方法為func1 return function () { // 以下稱這個方法為func2 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } var store = createStore.apply(undefined, args);// 調用createStore方法生成store
// 初始化一個新的dispatch,暫時認為_dispatch = store.dispatch,從效果上看是這樣的,暫時不明白為什麼這樣寫,有待大牛指點。 var _dispatch = function dispatch() { throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.'); }; var middlewareAPI = { getState: store.getState, dispatch: function dispatch() { return _dispatch.apply(undefined, arguments); } };
//讓每一個中間件調用一次,參數為middlewareAPI,並把結果方法chain中,註意這個地方,是中間件的第一層,參數是{getState, dispatch} var chain = middlewares.map(function (middleware) { return middleware(middlewareAPI); });
// 將各個中間件的功能組合到 dispatch 上,生成新的dispatch,註意此時是中間件的第二層, 參數是store.dispatch (關於 compose 解析) _dispatch = compose.apply(undefined, chain)(store.dispatch);
return _extends({},
store, // 最終返回的是store數據和加強後的dispatch {dispatch: _dispatch} ); }; }; }
a、applyMiddleware 方法本身
它首先通過一個for迴圈,將它的形參以數組元素的形式放到 middleware 中,並返回了一個形參為 createStore 方法(即為標註的func1);
b、形參為 createStore 方法(即為標註的func1)
這貨很懶,只是返回了一個有很多參數的方法(即為標註的func2);
c、很多參數的方法(即為標註的func2)
這個方法和 applyMiddleware 一樣,它首先通過一個for迴圈,將它的形參以數組元素的形式放到 args中;
具體源碼的解析,請看源碼的註釋;
中間件的編寫
我在源碼的解析中,寫了兩個註意,分別是中間件的第一層和第二層,多以中間件的外面兩層應該是如下的:
const timeOutMiddleware = ({ dispatch, getState }) => { return (storeDispatch) => { return { ... }; }; }
根據 compose 的操作原理,每一個中間件,即 chain 中的每一個元素,參數都是前一個中間件的組合後的 dispatch,返回的都是在參數的基礎上組合自己功能後的dispatch,chain最後一個元素參數是store.dispatch。因此return的應該是一個通過這些中間件加強後的 dispatch。在此,我們就可以根據action傳入的數據進行區分,加入我們中間件具體需要處理的情況。如下:
const timeOutMiddleware = ({ dispatch, getState }) => (storeDispatch) => (action) => { if (typeof action === 'function') { return action(dispatch, getState); } else { return storeDispatch(action); } };
如上,我們如果傳入正常的action,我們就執行正常的store.dispatch。如果我們 action 傳入的是 function 類型,那麼這就是我們中間件處理的情況了。在action的function中我們可以拿到 store 的 dispatch 和 store 的 getState。當然我們在這個方法中可以非同步的獲取服務端的數據,然後根據成功或者失敗的結果,通過 dispatch 再次分發一個正常的 action 同步我們獲取到的數據,執行相應的操作。如下
deleteStaff = (data) => { const { dispatch } = this.props; const { staffId } = data; const action = (dispatchd, getState) => { setTimeout(() => { dispatchd({type: 'DELETE', payload: {staffId}}); }, 3000); }; dispatch(action); };
這是一個方法,刪除一條數據,通過 setTimeout 模仿非同步,在3秒後,再次發一個 action,執行刪除數據;
本文慄子的代碼:https://github.com/wayaha/react-demos-middleware
(對您有幫助的話,請您幫我點顆 star)