函数复合是实现洋葱模型最直接的方式,通过高阶函数嵌套与从右到左的包裹顺序(如a(b(c(handler)))),使外层中间件最先调用、最后结束,天然形成“前置→next→后置”的两段式执行流。

函数复合是实现洋葱模型最直接、最本质的方式——它不依赖框架,只靠高阶函数的嵌套与顺序调用,就能自然还原“外→内→外”的执行路径。
核心原理:从 compose 到洋葱结构
洋葱模型的关键不是“有多少层”,而是“每层能控制进入和退出时机”。函数复合通过 从右到左依次包裹 的方式,让最外层中间件最先被调用、最后结束,形成天然的两段式执行(前置 → next → 后置):
- compose([a, b, c])(handler) 等价于 a(b(c(handler)))
- c 最靠近业务 handler,最先执行其前置逻辑,也最先执行其后置逻辑
- a 在最外层,前置逻辑最早运行,后置逻辑最晚运行,正好包住整个流程
中间件函数签名要统一
所有中间件必须遵循同一契约,才能被 compose 安全组合。推荐签名是:
(ctx, next) => { /* 前置逻辑 */; await next(); /* 后置逻辑 */ }
立即学习“前端免费学习笔记(深入)”;
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
- ctx 是贯穿全程的上下文对象,用于透传数据、修改响应、中断流程
- next 是由 compose 自动注入的“下一个中间件返回的函数”,调用它即进入内层
- 不调用 next 就等于截断流程,适合鉴权失败、日志拦截等场景
手写一个轻量 compose 支持异步
不需要引入库,几行代码就能支撑真实项目:
const compose = (middlewares) => (ctx) => {
let index = -1;
const dispatch = (i) => {
if (i <= index) return Promise.reject(new Error('next() called multiple times'));
index = i;
const fn = middlewares[i];
if (!fn) return Promise.resolve();
return Promise.resolve(fn(ctx, () => dispatch(i + 1)));
};
return dispatch(0);
};
- 用递归 dispatch 控制调用序号,避免重复调用 next 导致混乱
- 每个中间件拿到的 next 都是 dispatch(i + 1),天然形成链式闭环
- Promises 包裹确保 await next() 正确等待内层完成,再执行后置逻辑
典型使用模式:分层职责清晰
按洋葱层级组织中间件,越外层越通用,越内层越具体:
- 外层:请求日志、耗时统计、CORS 处理
- 中层:身份校验(JWT 解析)、权限检查(RBAC 判断)、请求体解析
- 内层:业务路由分发、具体控制器执行、错误统一兜底
这样任意一层都能在 request 进入时做预处理,在 response 返回前做增强,比如日志中间件既记录开始时间,又在 next 结束后补上响应码和耗时。

















