1、applyMiddleware 介紹
applyMiddleware 作爲 Redux 的核心 api 之一,本質就是在 dispatch 更改 reducer 之前做一些操作,具體的實現其實就對 store 的增強,其中最終是對 store 中的 dispatch 的增強。關於 applyMiddleware 的使用可參考上一章節 Redux源碼分析(1) - Redux介紹及使用 和 官方文檔 applyMiddleware 。
Middleware 可以讓你包裝 store 的 dispatch 方法來達到你想要的目的。同時, middleware 還擁有“可組合”這一關鍵特性。多個 middleware 可以被組合到一起使用,形成 middleware 鏈。其中,每個 middleware 都不需要關心鏈中它前後的 middleware 的任何信息。
applyMiddleware 的源碼結構圖如下圖所示。
首先看下 applyMiddleware 的使用方式,以當前 demo 爲實例:
// middleware
const Logger = (store) => (next) => (action) => {
console.info('logger start');
let result = next(action);
console.info('logger end');
};
const Test = (store) => (next) => (action) => {
console.info('test start');
let result = next(action);
console.info('test end');
};
let store = createStore(rootReducer, applyMiddleware(Logger, Test));
這裏涉及到兩個 api : createStore 、 applyMiddleware 。 在 createStore 的源碼分析中,有提到存在 enhancer 時,直接執行下面的語句。
return enhancer(createStore)(reducer, preloadedState);
配合當前實例可知。此時的 enhancer 就是 applyMiddleware(Logger, Test) 的結果。
2、applyMiddleware 源碼分析
applyMiddleware 的源碼如下:
import compose from './compose';
export default function applyMiddleware(...middlewares) {
/*
1、中間件的使用方式如下:let store = createStore(rootReducer, applyMiddleware(Logger, Test));
2、在 createStore 方法中,當存在 enhancer 時,執行語句 enhancer(createStore)(reducer, preloadedState);
3、可知 enhancer 即爲 applyMiddleware(Logger, Test) 的執行結果,也即下邊返回的高階函數。
*/
return createStore => (...args) => {
/*
1、此處即執行 enhancer 的執行。可知 createStore 參數即爲 redux 的 createStore方法, ...agrs 表示 reducer, preloadedState
2、store 即爲createStore方法在沒有enhancer的時候執行的結果。也即中間件作用前的原始store
*/
const store = createStore(...args);
// 定義了一個dispatch, 調用會 throw new Error(dispatching雖然構造middleware但不允許其他middleware應用 )
let dispatch = () => {
//……
};
// debugger
// 定義middlewareAPI, 傳遞 getState、 dispatch到中間件。這也是中間件中能訪問state的原因。
const middlewareAPI = {
getState: store.getState,
// 重新定義dispatch,此處不是直接賦值,是因爲 dispatch 是引用賦值,直接賦值的話,後邊更改dispatch會影響到原有的dispatch
dispatch: (...args) => dispatch(...args)
};
/*
1、中間件形式 store => next => action =>{},
2、執行後即返回 [ (next)=>acticon=>{...next(action)...}]的array,賦值給chain
3、以當前demo爲實例。及此時的Logger執行後的結果爲f1, 記 Test執行後的結果爲f2
*/
const chain = middlewares.map(middleware => middleware(middlewareAPI));
/*
1、compose(...funcs)執行後返回 : (...args)=>(funcA(funcB(...args)),其中funcA、func 爲 funcs的元素
2、以當前實例說明。則 compose(...chain) 執行的結果 (...args)=>(f1(f2(...args)) ,其中 f1、f2爲上邊定義的 Logger、Test執行的結果
3、compose(...chain)(store.dispatch) 執行的結果即爲 f1(f2(store.dispatch)) ,此時 f2(store.dispatch) 作爲了 f1 的next參數
4、由f1的形式可知。此時 dispatch = (action) => {... f2(store.dispatch) (action) ...}, 也即store 中的增強的dispatch
5、當執行 store.dispatch(action)時,實際執行的是 (action) => {... f2(store.dispatch) (action) ...}。
6、則此時 f2 會執行,其中 next參數爲store.dispatch,action參數爲action。
7、如果有多箇中間件。則可知增強的dispatch爲:f1(f2(f3(...fn(store.dispatch))))。
8、則f1執行時 next = f2(f3(...fn(store.dispatch))),next(action)即執行 f2(f3(...fn(store.dispatch)))(action)
9、此時即f2執行,此時next = f3(...fn(store.dispatch)),依次類推到最後next爲 store.dispatch。
10、由此也可知,中間件的函數中必須有next(action)語句的執行。
*/
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch // 返回增強的store
};
};
}
applyMiddleware 的源碼分析,註釋部分還算詳細。applyMiddleware 作用中間件之後返回一個高階函數,由源碼可知這個高階函數形式如下:
const enhancer = createStore => (...args) => { ....... } //語句(1)
enhancer 作爲參數傳入 createStore。當 createStore 方法中存在 enhancer 時會直接執行下邊語句:
return enhancer(createStore)(reducer, preloadedState); // 語句(2)
此時實際執行的高階函數就語句(1)的執行,其中傳入的參數依次是 createStore (redux 的 createStore 方法)和 reducer、 preloadedState。
applyMiddleware邏輯都在返回的高階函數中, 主要乾了以下幾件事:
2.1、 獲取原始store
const store = createStore(...args);
由上面的分析可知,createStore 即爲 redux 的 createStore 方法, …args 是 reducer 、preloadedState。所以這句話可以理解:在沒有中間件時,通過當前 reducer 創建的 store。在之前的章節中有提到過,中間件本質就是對 store 的增強 ,此處先拿到原始的 store 正是爲了後邊的增強做準備的。
2.2、中間件初始化
這裏叫中間件初始化,也不是很準確,可以理解爲中間件的執行,因爲中間件是一個高階函數,此處定義爲中間件的第一次執行,可與上文中的源碼結構圖對照。其對應的代碼如下:
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
middlewareAPI 中包含 getState 和 dispatch 屬性, middlewares 爲傳入的中間件列表,通過map執行後,返回的是形如:(next)=>acticon=>{…next(action)…} 的一個數組。以當前demo爲實例,分別執行 Logger、 Test,返回結果分別標記爲 f1、 f2 (爲了方便闡述)
let f1 = next => action =>{
// Logger
next.action()
}
let f2 = next => action =>{
// Test
next.action()
}
chain = [f1, f2]
2.3、中間件組合
export default function compose(...funcs) {
// ..... 邊界的判斷
const a = funcs.reduce((a, b) => {
return (...args) => a(b(...args));
});
return a;
}
先來看下 compose 方法的源碼。通過數組的 reduce 方法循環執行,實現函數的組合,最終返回一個複合函數。
compose(func1, func2, func3, ... ,funcn) 返回 (...args) => f1(f2(f3(....fn(args))))
2.4、 增強dispatch,並返回最新的store
dispatch = compose(...chain)(store.dispatch)
這句話是整個 applyMiddleware 代碼的關鍵。根據2.3的分析可知 compose(…chain) 執行的結果就是所有中間件函數第一執行(參數爲store)後的組合。
有上文可知: f1、f2 ...... fn 都是中間件第一執行之後返回的高階函數 :(next) => acticon => { ...next(action)...}
compose(...chain)(store.dispatch) 執行的結果可以理解爲 f1(f2(f3(....fn(store.dispatch)))的執行,此時 f1 的入參 next = f2(f3(....fn(store.dispatch)) ,執行後賦值 dispatch
dispatch = action => {
//...... before
f2(f3(....fn(store.dispatch))(action);
//...... after
}
組合後將 store.dispatch 作爲組合函數的入參,並返回一個新的 dispatch 函數,覆蓋 store 中原有的 dispatch 屬性,這既保持了與 createStore 輸出結果的一致性;也實現了 dispatch 的增強,到這裏也解釋 applyMiddleware 本質上是對 dispatch 的增強。
當執行 store.dispatch(action)時,此時的 dispatch 就是被增強的 dispatch,繼續分析
根據上邊分析,此時的dispatch爲:
dispatch = action => {
//...... before // (第1箇中間件的before)
f2(f3(....fn(store.dispatch)(action);
//...... after // (第1箇中間件的after)
}
執行 store.dispatch(action)後,
(1)首先執行:(第1箇中間件的before),
(2)繼而執行 f2(f3(....fn(store.dispatch))(action),此時 f2 函數的 next = f3(....fn(store.dispatch)。
f2 = next => action =>{
//...... before // (第2箇中間件的before)
next(action)
//...... after // (第2箇中間件的after)
}
(3)執行: (第2箇中間件的before)
(4)繼而執行 next(action),也即執行 f3(....fn(store.dispatch)(action)
(5) 以此類推:f1 ---- next = f2(f3(... fn(store.dispatch)))
f2 ---- next = f3(... fn(store.dispatch))
f3 ---- next = f4(... fn(store.dispatch))
fn ---- next = store.dispatch
......
(6) 執行:(第2箇中間件的after)
(7) 執行:(第1箇中間件的after)
以上部分的分析,也符合 Redux 官文文檔中,關於中間件的介紹。每個中間件中執行 next (action) 是保證中間件能夠形成 middleware 鏈 的關鍵。
…middlewares (arguments): 遵循 Redux middleware API 的函數。每個 middleware 接受 Store 的 dispatch 和 getState 函數作爲命名參數,並返回一個函數。該函數會被傳入 被稱爲 next 的下一個 middleware 的 dispatch 方法,並返回一個接收 action 的新函數,這個函數可以直接調用 next(action),或者在其他需要的時刻調用,甚至根本不去調用它。調用鏈中最後一個 middleware 會接受真實的 store 的 dispatch 方法作爲 next 參數,並藉此結束調用鏈。所以,middleware 的函數簽名是 ({ getState, dispatch }) => next => action
2.5、 洋蔥模型
通過上文的分析可知,中間件是在 dispatch 更改 reducer 之前做一些動作,根據 2.4 的可知,增強的 dispatch 在執行過程中,實際是:
其執行過程對應的就是洋蔥模型,先從外到裏層層深入直到最裏層,然後從最裏層層往外,直到最外層。
結合本文中實例。可知最後中間件 dispatch 時輸出的結果如下:
logger start
test start
test end
logger end
3、小結
- 中間件本質對是 store 的增強,準確來說是最 store 中的 dispatch 的增強;
- 中間件能夠形成組合鏈,依賴於 next(action) 的層層傳遞;
- 中間件的調用符合洋蔥模型,由外到裏再由裏到外。
Redux源碼分析(1) - Redux介紹及使用
redux源碼分析(2) - createStore
Redux源碼分析(3) - applyMiddleware