中间件(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 */functionisPlainObject(obj){if(typeofobj!='object'){returnfalse; } return Object.getPrototypeOf(obj) ===Object.prototype}/** * 生成一个6位的随机字符串 */functiongetRandomStr(len){returnMath.random().toString(36).substring(2,len+2).split("").join(".");}exportdefaultfunction(reducer,initState){ let currentReducer = reducer,currentState = initState; let listens =[];functiondispatch(action){//限制条件//action 必须是一个平面对象//必须有type属性if(!isPlainObject(action)){thrownewTypeError('action必须是平面对象'); } if(action.type ===undefined){thrownewTypeError('action对象必须有type属性'); } currentState =currentReducer(currentState,action);//store更新完state之后 依次调用添加的监听器for (const listen of listens) { listen(); } } //初始化store的时候会默认 dispatch一个特殊的type类型 dispatch({ type:`@@redux/INIT${getRandomStr(6)}`})functiongetState(){return currentState; } function subscribe(listen){ listens.push(listen); //如果已经取消监听了 则直接返回 let isRemove = false;//返回一个函数 执行清除监听器的操作returnfunction(){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 */exportdefaultfunction(obj,dispatch){if(typeof obj ==="function"){return getAutoDispatchAction(obj,dispatch) }elseif(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{thrownewTypeError("报错了"); }}functiongetAutoDispatchAction(actionCreate,dispatch){/** * 将参数汇总成数组 (arg1,arg2) 变成[arg1,arg2] * 函数内部展开是重新变成(arg1,arg2) (展开运算符) */returnfunction(...args){ const action = actionCreate(...args); dispatch(action); }}
applyMiddleware:
import compose from"./compose";/** * 外层函数捕捉中间件集合 * 中层函数接收创建原始厂库的函数 * 内层函数 用来增强原始厂库 增强原始的dispatch函数 */exportdefaultfunction(...middlewares){returnfunction(createStore){returnfunction(reducer,defaultState){ const store = createStore(reducer,defaultState); let dispatch = (...args)=>{thrownew 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:
exportdefaultfunction(...middlewares){returnmiddlewares.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';