参考答案:
Koa 和 Express 的核心差别不在 API 多寡,而在中间件的执行模型:Koa 的 next() 返回 Promise,Express 4 的 next() 返回 undefined。其余差异基本都是这一点的延伸。
Koa 是洋葱模型。中间件围绕 await next() 形成严格嵌套,调用下游前是下行阶段,下游全部完成后回到上行阶段:
1app.use(async (ctx, next) => { 2 const start = Date.now(); 3 await next(); // 等下游全部走完 4 ctx.set('X-Response-Time', Date.now() - start + 'ms'); 5});
await next() 之后还能改响应头,因为响应此刻还没发出。
Express 4 的 next() 只负责调用下一个中间件,返回 undefined,所以 await next() 等价于 await undefined,只让出一个微任务,并不等待下游的异步工作。同样的逻辑写在 Express 里,响应头加不上去 —— 下游已经把响应发走了。
用输出顺序可以直接验证。下面两段代码逻辑完全一样:中间件 A 在调用下游前后各打一条,中间件 B 等 50 毫秒后打一条。
1// Koa 2app.use(async (ctx, next) => { console.log('A1'); await next(); console.log('A2'); }); 3app.use(async (ctx) => { await new Promise(r => setTimeout(r, 50)); console.log('B'); ctx.body = 'ok'; }); 4 5// Express 4 6app.use(async (req, res, next) => { console.log('A1'); await next(); console.log('A2'); }); 7app.use(async (req, res) => { await new Promise(r => setTimeout(r, 50)); console.log('B'); res.end('ok'); });
1Koa: A1 -> B -> A2 2Express: A1 -> A2 -> B
(Koa 的输出已在 koa@2.14.1 实测。)需要注意:Express 在同步链下也会打出 A1 -> B -> A2,看起来像洋葱,但那只是 next() 同步调用带来的副作用;一旦下游含异步,await next() 就不等了。
Koa 从设计上就是为 async/await 准备的,中间件返回 Promise,同步 throw 和异步 rejection 都会沿 await next() 冒泡,外层用 try/catch 统一兜住:
1app.use(async (ctx, next) => { 2 try { 3 await next(); 4 } catch (err) { 5 ctx.status = 500; 6 ctx.body = { message: err.message }; 7 ctx.app.emit('error', err, ctx); // 交给统一日志 8 } 9});
Express 4 基于回调,中间件即使返回 Promise 框架也不接收,异步错误必须手动 next(err)。它靠四个参数的错误中间件识别错误:
1app.use((err, req, res, next) => { // 4 个参数 = 错误中间件 2 res.status(500).json({ message: err.message }); 3});
Express 4 中 async 函数里 throw,只会产生一个 rejected Promise,next() 没被调用,请求会一直挂到超时,同时打印 unhandledRejection。Express 5 开始原生接收中间件返回的 Promise,这个差距在缩小。
Express 把 Node 原生的 req / res 增强后交给中间件,功能分散在两个对象上:req.query、req.params、res.json()、res.status()。
Koa 提供统一的 ctx,内部用 getter/setter 代理 ctx.request 和 ctx.response:ctx.query、ctx.params、ctx.body、ctx.status。底层仍是 Node 的 req/res(ctx.req、ctx.res)。注意不要直接改 ctx.res,会绕过 Koa 的响应封装。
Express 内置路由、静态文件、模板引擎,开箱即用,中间件生态最大。Koa 核心很小,路由(@koa/router)、静态资源(koa-static)、body 解析(koa-bodyparser)都要自己装。这是刻意的取舍,不是缺陷。
不要因为「Koa 更新」就直接换。判断依据是团队的异步写法、生态依赖和维护成本:已有大量 Express 中间件的项目,迁移要替换几乎全部中间件,收益通常不足以覆盖成本;新项目且团队全面使用 async/await,Koa 的中间件模型确实更好用。
最近更新时间:2026-10-26

题库维护不易,您的支持就是我们最大的动力!