1.2 Koa 与 Express:对比与选型


1.2 Koa 与 Express:对比与选型

别以为 Koa 只是"更精简的 Express"——这是选型会上最常见的误判,也是本节要拆掉的第一块砖。上一节的历史告诉我们两者同源,这一节把它们放上同一张评审桌:内核怎么不同、生态怎么分工、错误模型差在哪,最后给出一张能带进会议室的选型判断表。

先厘清:同源的两者差在哪一层

Express 与 Koa 解决的是同一个问题——把 HTTP 请求组织成可维护的处理流程——但在"框架替你做多少"这个坐标上站在两端。Express 选择把常用能力内置:路由、静态文件、模板集成、错误中间件约定,装完就能开工。Koa 选择只内置控制流:内核只保留 Application、Context、中间件队列三样东西,路由、解析、静态服务统统外置成中间件,由你按需组装。

看同一个接口在两边长什么样:

// Express:内置路由,装完即用 const express = require('express'); const app = express(); app.get('/hello', (req, res) => { res.json({ msg: 'hi', name: req.query.name }); }); app.listen(3000);
// Koa:内核只给控制流,路由要装 koa-router const Koa = require('koa'); const Router = require('koa-router'); const app = new Koa(); const router = new Router(); router.get('/hello', (ctx) => { ctx.body = { msg: 'hi', name: ctx.query.name }; }); app.use(router.routes()).use(router.allowedMethods()); app.listen(3000);

Koa 版本多两行 require 与挂载,换来的是:路由器本身就是一个中间件层,你可以在这层之前塞鉴权、之后塞日志,层次完全由你排布。Express 不是做不到,但它的路由是"框架的一部分",插入点靠约定而非结构。

三个维度的正面对照

内核与请求模型。 Express 的处理函数拿 (req, res) 两个原生对象,直接读写;Koa 把原生对象包成 ctx,请求与响应统一从一个上下文进出,且 ctx 上的属性大量做了委托代理(细节在第 2.3 节展开)。后果是:Express 代码有时直接操作 res 的底层状态,Koa 代码则倾向于"给 ctx.body 赋值、让内核在出层时统一发送",响应时机更可预测。

错误模型。 Express 的错误中间件靠四参数签名识别,且只能捕获同步异常与手动 next(err);回调里的异常必须显式转交。Koa 的 async 中间件里 throw 的一切都会沿 Promise 链向上冒泡,最外层一个 try/catch 全接住。这不是风格差异,是"错误有没有机制性出口"的差异。

生态与团队惯性。 Express 生态更老、资料更多、招人更容易;Koa 生态质量高但需要自己选型组装。国内很多企业级框架(如 Egg)以 Koa 为内核,等于社区替你做了一轮中间件精选——如果你的团队用这类上层框架,选型讨论其实已经完成了一半。

维度 Express Koa
内核定位 微内核加常用功能内置 极简内核,一切能力外置
中间件签名 (req, res, next),同步为主 async (ctx, next),原生异步
控制流 单向流动,一去不回 洋葱模型,进层出层对称
错误出口 四参数错误中间件,同步异常为主 Promise 冒泡,最外层统一捕获
响应时机 各层可直接写 res,时机分散 出层统一发送,可预测
路由 内置 外置 koa-router,本身是中间件层
学习资料与招人 存量大,上手快 相对少,但内核源码极短可通读

怎么选:一张可执行的判断表

表格只能给方向,判断要落到具体场景。下面是我们在技术评审里实际使用的判断顺序:

  1. 团队已有大量 Express 存量代码,且近期没有重构窗口——留在 Express。框架迁移的成本大头从来不是 API 差异,而是行为差异的回归验证,没有窗口期就别开这个头。
  2. 异步链路复杂、错误处理是当前痛点——换 Koa。洋葱模型对"申请资源—使用—释放"这类流程的约束,正是复杂异步链路最缺的纪律。
  3. 要接 Egg 这类企业级上层框架——不用纠结,它们以 Koa 为内核,学 Koa 就是学它们的底层语言。
  4. 全新项目、接口以 JSON API 为主——两者都能胜任,此时比较的是团队口味:喜欢"全家桶约定"选 Express,喜欢"自己排层次"选 Koa。
  5. 极端性能敏感的网关类场景——两者差距在现代硬件上通常不是瓶颈,真正的瓶颈在业务逻辑与 IO;先做压测再谈框架替换。

我个人的倾向写在前面供参考:新起的 Node.js 后端服务,我更愿意从 Koa 起步。理由不是性能跑分,而是它逼着团队从第一天就想清楚"每一层做什么、顺序为什么这样排"——这个思考过程对任何框架都有益,只是 Koa 把它变成了必答题。

迁移的真实成本:一个案例的账

某内容平台把一个 Express 服务迁到 Koa,接口约莫两百个,两人全职做了三周。拆开账本看:真正的 API 改写只占三分之一时间;剩下的花在三类隐性成本上——中间件顺序重排引发的行为差异(bodyparser 的位置变了导致两处读取 body 为空)、错误处理从散落式改为统一捕获后监控告警规则的适配、以及团队从回调思维切换到洋葱思维的磨合。这份账的结论值得记住:迁移成本与代码量无关,与"隐式约定"的数量成正比。

常见坑:把"Express 中间件写法"直接搬进 Koa。Express 里 res.send() 一调用响应就发出去了;Koa 里必须给 ctx.body 赋值、等出层统一发送。带着旧肌肉记忆写 Koa,最容易出现"设置了 body 却拿到 404"这类怪现象——因为函数没等发送时机就结束了。

本节要点回顾

  • 两者同源而立场相反:Express 内置能力求快,Koa 外置能力求控制流的清晰;
  • 控制流是分水岭:单向流动对洋葱式对称,决定了资源管理与错误出口的形态;
  • 错误模型有机制性差异:Koa 的 Promise 冒泡让最外层捕获成为可能;
  • 选型看场景不看跑分:存量包袱、异步复杂度、上层框架绑定是三个决定性因素;
  • 迁移成本与隐式约定数量成正比:中间件顺序、响应时机、错误规则都要逐条对账。

下一节动手:从空目录搭出最小可跑的 Koa 应用,把本节纸面上的"洋葱"变成命令行里能看到的一次真实请求穿透。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U