跳到主要内容

Express、koa 实现原理以及对比:中间件、路由、上下文模型分别是什么?

面试速答(30 秒版 TL;DR)

  • Express 和 Koa 都是 Node Web 框架,但设计哲学明显不同。
  • Express 更像“基于中间件栈的实用框架”:核心是按顺序执行 req, res, next 中间件和路由层。
  • Koa 更像“更薄的一层 HTTP 抽象”:核心是 ctx 上下文对象和基于 Promise 的洋葱模型。
  • Express 早期设计更偏回调风格,中间件默认是“线性往后传”;Koa 从一开始就围绕 async/await 设计,天然适合写前后置逻辑。
  • 面试一句话:Express 解决的是“怎么快速组织路由和中间件”,Koa 更强调“怎么把请求处理抽象得更干净、更可组合”。

心智模型:两个框架都在做“把请求交给一串函数”

不管是 Express 还是 Koa,本质都在做同一类事:

  1. 收到 Node 原生 http.createServer 的请求。
  2. 构造一套更好用的请求/响应抽象。
  3. 把请求交给一串中间件和路由处理。
  4. 最终把结果写回原生 res

差异主要在两点:

  • 中间件执行模型 不一样。
  • 请求上下文抽象 不一样。

先看两者的总体结构

先记共同主链路:原生 http -> 框架接管 -> 中间件 / 路由 -> 业务处理 -> 写回响应

这张图看起来很普通,但面试官真正想听的是下面这两点:

  • Express 的“中间件链”更偏 一个一个往后传
  • Koa 的“中间件链”是 可进入、可返回的 Promise 洋葱模型

一、Express 的核心实现原理

1. 基于原生 http 再封装一层

Express 最终还是跑在 Node 原生 HTTP 服务之上。

可以把它简化理解成:

const http = require('node:http')

function createApp() {
const stack = []

function app(req, res) {
let index = 0

function next(err) {
const layer = stack[index++]
if (!layer) return
layer(req, res, next)
}

next()
}

app.use = function (fn) {
stack.push(fn)
}

return app
}

const app = createApp()
http.createServer(app).listen(3000)

真实 Express 当然更复杂,里面还会有:

  • Application
  • Router
  • Route
  • Layer

但面试时不用把源码类名全背出来,知道“Express 把中间件和路由都抽象成 layer,再按顺序匹配执行”就够了。

2. 中间件模型是 req, res, next

app.use((req, res, next) => {
console.log('middleware')
next()
})

核心特征:

  • reqres 直接暴露给你,偏底层、偏命令式。
  • 调用 next() 表示继续往后走。
  • 如果你不 next(),也不结束响应,请求就会挂住。

3. 路由本质也是特殊中间件

app.get('/users', (req, res) => {
res.json([{ id: 1 }])
})

面试口径:

  • Express 会先判断路径、方法是否匹配。
  • 匹配成功后执行对应的 route handler。
  • 所以路由可以理解成“带匹配条件的中间件层”。

二、Koa 的核心实现原理

1. Koa 仍然基于原生 HTTP,但中间件设计完全不同

Koa 可以被简化理解成:

const http = require('node:http')

function createApp() {
const middlewares = []

const app = {
use(fn) {
middlewares.push(fn)
},
callback() {
const fn = compose(middlewares)
return function (req, res) {
const ctx = createContext(req, res)
fn(ctx).then(() => respond(ctx))
}
},
}

return app
}

http.createServer(app.callback()).listen(3000)

这里最关键的是两件事:

  • 先把原生 reqres 封装成 ctx
  • 再把中间件数组通过 compose 组合成 Promise 链。

2. 中间件模型是 ctx, next

app.use(async (ctx, next) => {
console.log('before')
await next()
console.log('after')
})

这意味着:

  • 你不再主要直接操作 reqres,而是操作 ctx
  • await next() 让你天然拥有前置和后置逻辑。
  • 错误处理、日志统计、事务包裹更自然。

3. ctx 是 Koa 很重要的抽象

Koa 会把:

  • req 封装成更易用的 request
  • res 封装成更易用的 response
  • 再挂到统一的 ctx

所以你常写的是:

ctx.status = 200
ctx.body = { ok: true }

而不是像 Express 那样频繁直接 res.status(...).json(...)


三、Express 和 Koa 的中间件差异为什么这么大

Express:线性传递

Express 的常见理解方式是:

  • 执行当前中间件
  • next()
  • 继续下一个

如果要做“后置逻辑”,写起来往往不如 Koa 自然,因为早期设计不是围绕 Promise 洋葱模型展开的。

Koa:洋葱模型

Koa 的中间件会形成:

before1 -> before2 -> before3 -> after3 -> after2 -> after1

所以统一日志、异常处理、资源释放会很顺手。


四、对比表:面试最常用的说法

维度ExpressKoa
中间件签名req, res, nextctx, next
执行模型顺序向后传递Promise 洋葱模型
异步编程风格历史上更偏回调,现代也支持 async从设计上就围绕 async/await
请求上下文直接操作 reqres通过 ctx 统一抽象
后置逻辑书写可以做,但不如 Koa 自然非常自然
框架风格功能更全、更直接更轻、更薄、更克制
生态更老、更成熟、历史包袱也更多更简洁,常配合社区中间件使用

五、什么时候选 Express,什么时候选 Koa

更适合 Express 的场景

  • 需要成熟、现成、开箱即用的生态。
  • 团队成员对 Express 更熟。
  • 项目中已经大量使用 Express 中间件。

更适合 Koa 的场景

  • 希望中间件模型更现代、更清晰。
  • 需要大量统一前后置逻辑,比如日志、鉴权、异常、响应包装。
  • 团队能接受“框架更薄,很多能力自己组装”。

面试别说成“谁全面碾压谁”,更稳的表述是:

  • Express 偏工程实用和生态积累。
  • Koa 偏抽象设计和中间件模型优雅。

典型题 & 标准答法

Q1:Express 的实现原理是什么?

  • 本质是基于 Node 原生 HTTP 服务封装。
  • 内部维护中间件和路由栈。
  • 请求到来后按顺序匹配 layer,并把 reqresnext 传给处理函数。

Q2:Koa 的实现原理是什么?

  • 也是基于 Node 原生 HTTP。
  • 先构造 ctx 上下文。
  • 再通过 compose 把中间件组装成 Promise 洋葱链。
  • 最终把 ctx.body 等结果写回响应。

Q3:Express 和 Koa 最大差别是什么?

  • Express 更偏线性中间件和直接操作 req/res
  • Koa 更偏 ctx 抽象和洋葱模型。
  • 从可组合性和前后置逻辑体验来看,Koa 更优雅。

Q4:为什么很多人说 Koa 更先进?

  • 主要是说它的中间件模型和异步抽象更现代。
  • 不是说 Express 不能用,而是 Koa 在设计上更克制、更统一。

易错点 / 坑

  • 把 Express 说成“没有中间件,只有路由”,这是错的。
  • 把 Koa 说成“只是把 req/res 改名成 ctx”,这太浅了。
  • 忽略两者中间件执行模型的差异,只会罗列 API。
  • 把“谁更好”讲成绝对结论,而不结合团队生态和场景。
  • 以为 Koa 内置了很多功能,实际上它故意保持更轻量。

速记要点(可背诵)

  • 两者都基于 Node 原生 http
  • Express 核心是 layer 栈 + req, res, next
  • Koa 核心是 ctx + compose + 洋葱模型
  • Express 更偏实用生态,Koa 更偏抽象优雅。
  • 真正的面试分水岭在于:你能不能把“中间件模型差异”讲透。