詳解中間件
中間件(Middleware)是軟件開發中一種常見的設計模式,其核心思想是?在核心邏輯的執行過程中插入可擴展的附加功能模塊?。以下是中間件的核心概念和特點:
?一、中間件的本質
中間件本質上是 ?"業務邏輯的增強層"?,它像流水線上的加工環節,對輸入的數據或事件進行預處理、攔截或后處理,但不改變核心邏輯本身。
?二、典型特征?
1、鏈式調用(洋蔥模型)
多個中間件形成處理鏈,依次傳遞處理權,可以同時處理請求(Request)和響應(Response),類似流水線作業。
2、可插拔性
中間件可按需組合,靈活添加或移除功能模塊,無需修改核心代碼。
3、職責單一原則
每個中間件專注一個獨立功能(如日志記錄、權限驗證等)。
?三、核心工作原理
假設一個包含三個中間件的處理流程(偽代碼):
// 中間件鏈式調用邏輯 const middlewareChain = [ (next) => (input) => { console.log("Middleware 1開始"); next(input); // 傳遞到下一個中間件 console.log("Middleware 1結束"); }, (next) => (input) => { console.log("Middleware 2開始"); next(input); console.log("Middleware 2結束"); }, (next) => (input) => { console.log("核心邏輯執行"); return "處理結果"; } ]; // 輸出結果: // Middleware 1開始 // Middleware 2開始 // 核心邏輯執行 // Middleware 2結束 // Middleware 1結束
下面是一個手寫redux的例子
createStore:
/** * 判斷是否是平面對象 * @param {*} obj */ function isPlainObject(obj){ if(typeof obj!='object'){ return false; } return Object.getPrototypeOf(obj) === Object.prototype } /** * 生成一個6位的隨機字符串 */ function getRandomStr(len){ return Math.random().toString(36).substring(2,len+2).split("").join("."); } export default function(reducer,initState){ let currentReducer = reducer,currentState = initState; let listens = []; function dispatch(action){ //限制條件 //action 必須是一個平面對象 //必須有type屬性 if(!isPlainObject(action)){ throw new TypeError('action必須是平面對象'); } if(action.type === undefined){ throw new TypeError('action對象必須有type屬性'); } currentState = currentReducer(currentState,action); //store更新完state之后 依次調用添加的監聽器 for (const listen of listens) { listen(); } } //初始化store的時候會默認 dispatch一個特殊的type類型 dispatch({ type:`@@redux/INIT${getRandomStr(6)}` }) function getState(){ return currentState; } function subscribe(listen){ listens.push(listen); //如果已經取消監聽了 則直接返回 let isRemove = false; //返回一個函數 執行清除監聽器的操作 return function(){ if(isRemove){ return; } const index = listens.indexOf(listen); listens.splice(index,1); isRemove = true; } } return { dispatch, getState, subscribe } }
bindActionCreators:
/** * //bindActionCreators 直接接收一個action創建函數 和 store.dispatch 如果是一個函數則直接調用 內部去dispatch action //如果接收的是一個對象 返回的跟對象屬性相同的數據結構 內部去dispatch action */ export default function(obj,dispatch){ if(typeof obj === "function"){ return getAutoDispatchAction(obj,dispatch) }else if(typeof obj === "object"){ let result = {}; for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const actionCreater = obj[key]; result[key] = getAutoDispatchAction(actionCreater,dispatch); } } return result; }else{ throw new TypeError("報錯了"); } } function getAutoDispatchAction(actionCreate,dispatch){ /** * 將參數匯總成數組 (arg1,arg2) 變成[arg1,arg2] * 函數內部展開是重新變成(arg1,arg2) (展開運算符) */ return function(...args){ const action = actionCreate(...args); dispatch(action); } }
applyMiddleware:
import compose from "./compose"; /** * 外層函數捕捉中間件集合 * 中層函數接收創建原始廠庫的函數 * 內層函數 用來增強原始廠庫 增強原始的dispatch函數 */ export default function(...middlewares){ return function(createStore){ return function(reducer,defaultState){ const store = createStore(reducer,defaultState); let dispatch = (...args)=>{ throw new Error("Dispatching while constructing your middleware is not allowed. "); }; const simpleStore = { getState:store.getState, dispatch:(...arge) => dispatch(...arge) } //返回的是創建dispatch函數的集合 每一個元素都是一個函數 調用函數會返回dispatch函數 const dispatchProducers = middlewares.map((mid)=>mid(simpleStore)); console.log(dispatchProducers) dispatch = compose(...dispatchProducers)(store.dispatch) return { ...store, dispatch } } } }
compose:
export default function(...middlewares){ return middlewares.reduce((a,b)=>(...args)=>a(b(...args))) // return function(...args){ // let lastReturn = null; // for (let index = middlewares.length-1; index >=0; index--) { // //最后一個 將默認的dispatch傳遞給最后一個中間件 該中間件調用返回一個新的dispatch 并將這個新的dispatch作為參數傳遞給下一個中間件 // if(index == middlewares.length-1){ // //該函數接收一個dispatch函數 并返回一個新的dispatch函數 // lastReturn = middlewares[index](...args) // }else{ // lastReturn = middlewares[index](lastReturn); // } // lastReturn.displayName = `applayMiddle-${index}`; // } // return lastReturn; // } }
中間件的組合邏輯:
其核心原理確實與閉包密切相關。具體執行流程如下:
- ?中間件鏈式調用機制?:
- 代碼通過倒序循環(從最后一個中間件開始)構建調用鏈
- 每個中間件接收前一個中間件返回的函數作為參數(即
next參數) - 最終返回的是包裝了所有中間件邏輯的新dispatch函數
- ?閉包的關鍵作用?:
- 每個中間件函數都通過閉包保存了對前一個中間件返回函數的引用
- 當調用最終返回的dispatch時,會觸發第一個中間件的執行,其內部的
next()會調用閉包保存的下一個中間件 - 這種鏈式調用會一直傳遞到原始dispatch(即代碼中的
...args)
這種設計模式被稱為"洋蔥模型",請求從外層中間件進入,響應從內層中間件返回。閉包機制確保了每個中間件都能訪問到正確的next函數引用,從而形成完整的調用鏈。
最后統一導出:
export {default as createStore} from './createStore';
export {default as bindActionCreators} from './bindActionCreators';
export {default as combineReducers} from './combineReducers';
export {default as applyMiddleware} from './applyMiddleware';

浙公網安備 33010602011771號