中間件是什麼
如果你用過express.js之類的web框架,對中間件(Middleware)這個概念可能不會陌生。中間件其實就是一種獨立運行於各個框架組件之間的膠水代碼。在Express.js或Koa等框架中,中間件通常是運行在收到請求到處理請求之間,可是實現日誌記錄、身份認證等預處理操作。而在Redux裏,中間件是運行在action發送出去,到達reducer之間的一段代碼。
編寫中間件
日誌記錄是開發過程中常用的一個功能,你可以選擇侵入業務邏輯來記錄日誌(不推薦),也可以選擇使用中間件來實現這個功能。接下來讓我們編寫一個常用的日誌記錄中間件:
const loggerMiddleware = store => next => action => {
console.group(action.type);
console.log('action: ', action);
const result = next(action);
console.log('next state: ', store.getState());
console.groupEnd(action.type);
return result;
}
使用中間件的時候,要在初始化store的時候利用applyMiddleware
注入進去:
const store = createStore(
rootReducer,
applyMiddleware(loggerMiddleware)
)
這樣,我們在每次觸發action的時候就能記錄我們所需要的信息。同樣的方式,也可以實現日誌上報等功能。
組合中間件
中間件其實是一種高層次的抽象,可以將核心領域業務和基礎架構邏輯解耦開來。而多箇中間件可以組合使用,從而使每一箇中間件能夠保持“小而美”的特性。
const store = createStore(
rootReducer,
applyMiddleware(thunk, loggerMiddleware)
)
其中applyMiddleware
函數可以接收多箇中間件,源碼如下:
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
var store = createStore(reducer, preloadedState, enhancer)
var dispatch = store.dispatch
var chain = []
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
const last = funcs[funcs.length - 1]
const rest = funcs.slice(0, -1)
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}
我們可以看到它主要做了幾個工作:
- 返回一個高階函數,這個函數中會初始化store和重寫dispatch邏輯,以便後續使用。
- 將
store.getState
,dispatch
傳入每個中間件中,並收集調用鏈結果。
之後在應用中所有使用的dispatch都將是修改過的邏輯,從中我們可以看出有點面向切面編程
的味道,可以好好體會一下。