Hono与Express、Fastify、Elysia对比

要点

  • Express、Fastify、Hono、Elysia 分别诞生于不同年代,设计目标各异——选型之前先搞清楚它们各自在解决什么问题
  • 中间件模型是框架之间最影响日常开发体验的差异:Express 线性回调、Hono 洋葱模型、Fastify 生命周期钩子、Elysia 插件封装
  • 类型推导深度决定了你能在多大程度上依赖编辑器而不是文档:Express 几乎为零,Elysia 端到端覆盖
  • 运行时支持直接决定部署模型:Node.js 绑定还是 Edge 可部署,是架构决策而不是偏好
  • 性能差异在绝大多数业务场景中不构成选型依据——生态成熟度、团队经验、部署目标才是关键权重
  • 没有「最好」的框架,只有和当前场景、团队能力、运维条件匹配度最高的框架

1. 四个框架的定位与设计哲学

每个框架都是对特定问题的回答。理解它们「为什么被造出来」,比记住 API 差异更重要。

Express:Node.js 的最小公倍数

2010 年发布,是 Node.js Web 框架的事实标准。核心设计就一句话:一切皆中间件

Express 本身几乎不替你做决定——不内置验证、不内置序列化、不内置类型系统。它提供路由分发和中间件编排,剩下的事交给社区。这种极简主义让它活了十几年,也让它成了无数教程的起点。

代价也很明显:Node.js 生态里大量 Express 的「最佳实践」其实是补它缺失功能的补丁。body-parser、cors、helmet 这些几乎是项目标配的中间件,本质上都在弥补 Express 不提供开箱即用的事实。

Fastify:Schema 驱动的高性能替代

2017 年由 Node.js 核心团队成员创建,目标很明确:做一个比 Express 更快、更规范的 Node.js 框架

Fastify 的核心思路是用 JSON Schema 驱动请求验证和响应序列化。你在注册路由时声明输入输出的 Schema,Fastify 用 ajv 做验证、用 fast-json-stringify 做序列化——两者都比手写 JSON.parse / JSON.stringify 快。

另一个重要设计是插件系统。Fastify 的插件有独立的上下文封装(encapsulation),子插件的装饰器和钩子不会污染父级。这套机制比 Express 的全局中间件栈更有结构感,但学习成本也更高。

Hono:Web Standards 与多运行时

2022 年发布,设计目标是用 Web 标准 API 构建一个能跑在任何 JavaScript 运行时上的框架

Hono 的路由处理函数接收标准的 Request,返回标准的 Response。中间件基于 async/await 的洋葱模型。没有 req/res 拆分,没有 next() 回调,没有框架私有类型——你在 Hono 里写的代码,理论上可以直接搬到任何支持 Fetch API 的环境。

多运行时支持不是噱头。同一个 Hono 应用可以部署到 Cloudflare Workers、Deno Deploy、Bun、Node.js,不需要改任何代码。这在 Edge Computing 兴起的背景下是实际的架构优势。

Elysia:Bun 原生的端到端类型安全

2023 年发布,专门为 Bun 运行时设计。核心卖点是端到端类型安全:后端定义路由和验证 Schema,前端通过 Eden RPC 调用时自动获得完整的类型推导——参数类型、返回类型、错误类型全链路覆盖。

Elysia 的 API 风格受 tRPC 和 Hono 影响,但走得更远。它用装饰器风格的链式 API 做请求验证,用单例模式管理插件状态。性能在 Bun 上非常出色,但代价是完全绑定 Bun 运行时。

2. 多维度对比

一张表看完核心差异,下面逐项展开。

维度ExpressFastifyHonoElysia
首次发布2010201720222023
核心定位最小主义 Web 框架高性能 Schema 驱动框架Web Standards 多运行时框架Bun 原生端到端类型安全框架
运行时支持Node.jsNode.jsNode.js / Bun / Deno / Cloudflare Workers / Fastly 等仅 Bun
包体积~200KB~500KB(含 ajv)~14KB(核心)~200KB
TypeScript 支持社区类型定义 @types/express内置,Schema 驱动部分推导内置,完整推导 + RPC 类型内置,端到端推导 + Eden RPC
中间件模型线性 next() 回调生命周期钩子 + 插件封装洋葱模型 await next()插件系统 + 钩子
请求验证无内置,依赖中间件内置 JSON Schema(ajv)内置 validator 中间件内置装饰器验证
路由匹配数组遍历radix treeradix tree + 正则radix tree
序列化JSON.stringifyfast-json-stringifyJSON.stringify / Streaming APIJSON.stringify / Streaming API
部署模型传统服务器 / 容器传统服务器 / 容器服务器 / Edge / 多运行时Bun 服务器
WebSocket需配合 ws/socket.io内置内置 helper内置
ORM 集成生态丰富生态较好生态发展中生态早期

体积差异的实际意义

Hono 核心包只有 ~14KB,这不是为了跑分。在 Edge 环境(Cloudflare Workers、Deno Deploy)中,冷启动时间和包大小直接影响响应延迟和成本。Express 的 ~200KB 在传统服务器上没有感知,在 Edge 上会被放大。

3. 类型系统对比

类型推导是近几年 Web 框架竞争最激烈的维度。四个框架在这个方向上差异显著。

Express:类型基本靠手写

Express 本身不提供类型推导。@types/express 定义了 RequestResponseNextFunction 的基础类型,但路由参数的类型、请求体的类型、中间件注入的数据类型,都需要手动声明。

// Express — 手动声明是常态
app.get('/users/:id', (req: Request, res: Response) => {
  const id = req.params.id   // string,但你想让它更具体
  const body = req.body      // any — 完全没有类型保护
  res.json({ id, body })
})

这意味着 Express 项目的类型安全主要靠团队纪律和 Zod/Yup 等第三方库在运行时补。

Fastify:Schema 驱动的部分推导

Fastify 通过路由级别的 JSON Schema 做类型推导。你定义了 Schema,req.bodyreq.queryreq.params 的类型会自动推导。

// Fastify — Schema 驱动类型
server.get<{
  Querystring: { page: number }
  Reply: { users: User[] }
}>('/users', {
  schema: {
    querystring: { type: 'object', properties: { page: { type: 'integer' } } },
    response: { 200: { type: 'object', properties: { users: { type: 'array' } } } }
  }
}, async (request, reply) => {
  request.query.page // number — 从 Schema 推导
  return { users: [] }
})

推导是有的,但写法冗长。你需要同时维护 TypeScript 泛型和 JSON Schema 两套声明,存在重复定义的问题。

Hono:完整的请求-响应推导 + RPC

Hono 的类型系统覆盖三个层面:

  1. 路由参数推导:id 自动推导为 string
  2. 中间件类型传播c.set('user', user) 写入的类型,后续路由 c.get('user') 自动推导
  3. RPC 类型传递:通过 hono/client,后端路由定义的类型在前端调用时完整保留
// Hono — 后端定义
const app = new Hono()
  .get('/users/:id', zValidator('param', z.object({ id: z.string() })), (c) => {
    const { id } = c.req.valid('param') // { id: string } — 从 Zod Schema 推导
    return c.json({ id, name: 'Alice' })
  })
 
// Hono — 前端 RPC
const client = hc<typeof app>('http://localhost:8787')
const res = await client.users[':id'].$get({ param: { id: '1' } })
const data = await res.json() // { id: string, name: string } — 完整推导

不需要手写 JSON Schema,用 Zod 等库做验证的同时自动生成类型。前后端类型一致性由编译器保证。

Elysia:端到端类型安全

Elysia 和 Hono 一样支持端到端类型推导,但它的 Eden RPC 是内置的,不需要额外安装 hono/client

// Elysia — 后端定义
const app = new Elysia()
  .get('/users/:id', ({ params }) => {
    return { id: params.id, name: 'Alice' }
  })
 
// Elysia — 前端 Eden RPC
const client = edenTreaty<typeof app>('http://localhost:3000')
const { data } = await client.users[':id'].get({ params: { id: '1' } })
// data: { id: string, name: string }

Elysia 的验证用装饰器风格的链式 API,不需要 Zod(但也支持接入):

const app = new Elysia()
  .post('/users', ({ body }) => body, {
    body: t.Object({
      name: t.String({ minLength: 1 }),
      age: t.Number({ minimum: 0 })
    })
  })

类型系统对比总结

维度ExpressFastifyHonoElysia
路由参数类型手动手动或 Schema自动推导自动推导
请求体类型anySchema 推导Validator 推导装饰器推导
响应类型手动Schema 声明自动推导自动推导
中间件类型传播有限完整(c.set/c.get通过插件上下文
前端 RPChono/clientEden(内置)
端到端类型安全

4. 中间件模型对比

中间件模型决定了你写代码时的思维模式。这是四个框架之间最影响日常开发体验的差异。

Express:线性 next() 回调

Express 的中间件是一个函数签名 (req, res, next) => void 的回调队列。每个中间件可以选择处理请求、修改 req/res,然后调用 next() 把控制权交给下一个中间件。

app.use(authMiddleware)    // 1. 验证身份
app.use(rateLimiter)       // 2. 限流
app.get('/api/data', handler) // 3. 处理请求

这种模型简单直接,但有两个常见问题:

  1. 错误处理依赖 next(error):如果你忘了传 error 给 next,错误会被吞掉
  2. 没有统一的「后处理」机制:Express 中间件在 next() 之后无法再修改响应,因为响应可能已经被下游发送了

Hono:洋葱模型

Hono 的中间件基于 await next(),形成洋葱模型。请求从外到内穿过中间件层,响应从内到外穿回来。

app.use('*', async (c, next) => {
  const start = Date.now()
  await next()              // 请求向内传递
  const ms = Date.now() - start
  c.header('X-Response-Time', `${ms}ms`)  // 响应向外传出时可以修改
})

洋葱模型的优势是前后处理对称:你可以在 await next() 之前做前置逻辑(解析 token、记录开始时间),在之后做后置逻辑(计算耗时、修改响应头)。这种结构天然适合日志、监控、压缩等横切关注点。

错误处理也更自然——中间件里的 throw 会自动冒泡到 app.onError(),不需要手动 next(error)

Fastify:生命周期钩子 + 插件封装

Fastify 没有传统意义上的「洋葱模型」。它把请求处理拆成了多个生命周期钩子(onRequestpreParsingpreValidationpreHandleronSendonResponse),你可以在每个阶段插入逻辑。

fastify.addHook('onRequest', async (request, reply) => {
  // 请求到达时执行
})
fastify.addHook('preHandler', async (request, reply) => {
  // 路由处理前执行
})
fastify.addHook('onSend', async (request, reply, payload) => {
  // 响应发送前执行,可以修改 payload
  return payload
})

Fastify 的插件系统有上下文封装:子插件里注册的钩子、装饰器只在子插件内生效,不会污染全局。这解决了 Express 全局中间件栈的命名冲突问题,但学习曲线更陡。

Elysia:插件 + 生命周期钩子

Elysia 结合了插件系统和钩子,风格接近 Fastify 但更紧凑。插件可以声明自己的生命周期钩子,通过 .group() 做作用域隔离。

const authPlugin = new Elysia({ name: 'auth' })
  .onBeforeHandle(({ headers, set }) => {
    if (!headers.authorization) {
      set.status = 401
      throw new Error('Unauthorized')
    }
  })
 
app.use(authPlugin)

Elysia 的 onBeforeHandle 类似 Fastify 的 preHandleronAfterHandle 类似 onSend。概念上更接近 Fastify,API 上更简洁。

中间件模型对比总结

维度ExpressFastifyHonoElysia
模型类型线性回调生命周期钩子洋葱模型插件 + 钩子
请求流转next()钩子按阶段执行await next()钩子按阶段执行
后处理能力无(响应已发送)onSend 钩子await next() 之后onAfterHandle 钩子
错误处理next(error)钩子内 throw自动冒泡到 onError自动冒泡到 onError
作用域隔离无(全局栈)插件封装路由级 app.route()插件 + .group()
学习成本中高

5. 性能对比

性能是框架选型时经常被高估的维度。先看数据,再谈数据的实际意义。

基准测试数据

以下数据引用自社区基准测试(具体数值会随版本和测试环境变化,这里给出数量级参考):

指标ExpressFastifyHono (Node.js)Elysia (Bun)
简单 JSON 响应 (req/s)~15,000~65,000~40,000~160,000
带路由参数的请求 (req/s)~12,000~55,000~35,000~140,000
带 5 个中间件 (req/s)~8,000~40,000~25,000~100,000
内存占用 (MB)~60~80~45~50

数据来源参考:TechEmpower Web Framework BenchmarksFastify 官方基准、社区独立测试。数值为近似量级,非精确对比。

数据背后的事实

几个值得注意的点:

  1. Express 最慢,但「慢」是相对的。15,000 req/s 对大多数业务系统意味着单机可以支撑数千并发用户。如果你的 QPS 不超过 1,000,四个框架的性能差异不会体现在用户感知上。

  2. Fastify 的高性能主要来自序列化fast-json-stringify 根据 Schema 预编译序列化函数,比 JSON.stringify 快 2-3 倍。如果你的接口返回复杂对象,这个优化有实际价值。

  3. Elysia 的绝对数字最高,但前提是 Bun。Bun 本身比 Node.js 快,所以 Elysia 的高性能有一部分是运行时的功劳,不完全是框架的功劳。

  4. Hono 在 Edge 上的延迟优势来自地理位置。Edge 部署把计算推到离用户最近的节点,网络延迟的减少(通常 50-200ms)远大于框架层面的性能差异。

什么时候性能才是选型依据

  • 单机需要支撑 10 万+ QPS 的网关或代理层 → Fastify 或 Elysia
  • 需要全球低延迟的 API → Hono + Edge 部署
  • 普通业务系统(QPS < 5,000)→ 性能不是选型因素

6. 生态与社区

框架不是孤岛。插件数量、文档质量、社区活跃度直接影响你的开发效率。

npm 周下载量(2026 年近似值)

框架周下载量说明
Express~3,000 万Node.js 生态最大的 Web 框架,存量项目极多
Fastify~600 万Node.js 高性能替代方案,增长稳定
Hono~200 万多运行时场景增长快,Edge 生态核心选择
Elysia~20 万Bun 生态早期,增长与 Bun 采用率强相关

插件与中间件生态

框架官方插件社区插件说明
Express极多(数千个)十几年积累,几乎任何需求都有现成中间件
Fastify~50 个~500 个官方维护质量高,社区活跃度中等
Hono~30 个~200 个官方 helper 质量高,社区在快速增长
Elysia~20 个~50 个生态早期,核心功能多内置

文档质量

框架文档评分说明
Express中等历史悠久,部分文档陈旧,迁移指南不够系统
Fastify文档详尽,概念解释清晰,版本管理好
Hono简洁实用,示例丰富,多运行时部署指南是亮点
Elysia中高内容全面但版本迭代快,部分文档和代码不同步

生态成熟度是一个容易被忽视但影响深远的因素。Express 的生态意味着你遇到的任何问题都能在 Stack Overflow 找到答案。Hono 和 Elysia 的生态意味着你可能需要自己写一些集成代码。

7. 场景选型建议

没有「最好」的框架。选型的核心是匹配你的场景约束。

场景推荐框架原因
传统 Node.js 后端服务,团队熟悉 ExpressExpress生态最成熟,招人容易,资料最多
高吞吐 API 服务,Node.js 部署FastifySchema 驱动的高性能,插件封装有结构感
多部署目标(Edge + 服务器),Web Standards 优先Hono唯一真正跨运行时的框架,Edge 部署原生支持
Cloudflare Workers / Deno Deploy 项目Hono对 Edge 运行时的支持和文档最完善
Bun 项目 + 前后端全栈类型安全Elysia端到端类型安全体验最好,Bun 性能最优
微服务 / Serverless 函数Hono / FastifyHono 冷启动快、包小;Fastify 在 Node.js Serverless 上成熟
快速原型 / MVPExpress / HonoExpress 生态现成方案多;Hono 代码量少、上手快
已有大量 Express 中间件的遗留项目Express迁移成本高,收益不确定
需要完整前端 RPC 类型推导的全栈项目Elysia / HonoEden RPC 和 hono/client 提供端到端类型安全

几个常见的选型误区

「Express 太老了,应该用新的」 — 老不是缺点。Express 的中间件模型被后来所有框架借鉴。如果你的项目不需要 Edge 部署、不需要端到端类型安全,Express 仍然是可靠的选择。

「性能测试显示 X 比 Y 快 5 倍」 — 实验室性能在真实业务中很难复现。你的数据库查询、网络调用、序列化复杂度对整体延迟的影响远大于框架层面的差异。

「框架越新越好」 — 新框架意味着更少的生产验证、更薄的生态、更可能遇到未解决的 edge case。选型要看团队能承受的试错成本。

8. 本分组总结

「03-Hono 核心工作原理」到这里结束了。回顾一下这个分组的学习路径:

  • 01-03:从 new Hono() 的本质出发,理解 app 对象的内部结构,然后展开路由注册和匹配的底层机制——正则树和 radix tree 如何工作
  • 04-06:深入请求处理的核心三角——Context 上下文对象、HonoRequest 封装、Response 生成流程
  • 07-09:中间件体系——洋葱模型的执行顺序、next() 函数的调用链、错误冒泡与异常处理
  • 10-12:边界情况处理——notFoundonError 机制、多运行时适配原理、Edge 运行的技术基础

这 12 篇从「Hono 是什么」讲到「Hono 为什么能这样工作」。你现在知道了 c.req 背后的封装逻辑、中间件洋葱模型的真实执行流程、错误是怎么一层层冒泡到 onError 的、同一个 app 为什么能跑在 Workers 和 Node.js 上。

这些知识不是孤立的。当你在 04 篇里学了 c.set() / c.get(),到 07 篇的中间件洋葱模型里就明白了它们在请求生命周期中的位置。当你在 11 篇里理解了多运行时适配,12 篇的 Edge 运行就有了技术基础。整个分组是一个层层递进的结构。

延伸阅读

总结

Express、Fastify、Hono、Elysia 四个框架分别代表了 Node.js Web 开发在不同阶段的思路演进:从 Express 的最小中间件模型,到 Fastify 的 Schema 驱动高性能,到 Hono 的 Web Standards 多运行时,到 Elysia 的端到端类型安全。

选型的核心不是「谁最快」或「谁最新」,而是:

  1. 运行时约束:需要部署到 Edge 就选 Hono,绑定 Bun 就选 Elysia,Node.js 传统部署就在 Express 和 Fastify 之间选
  2. 类型安全需求:需要全栈类型推导就选 Hono 或 Elysia,否则 Express/Fastify 够用
  3. 生态成熟度:遗留项目和快速原型优先考虑 Express,新项目可以接受生态薄一些就选 Hono 或 Elysia
  4. 团队经验:团队熟悉的框架比「理论上最好」的框架更有生产力

这个分组从 Hono 的内部原理出发,最后回到框架选型的务实讨论。理解了 Hono 为什么能跨运行时、为什么类型系统这样设计、为什么中间件用洋葱模型——这些「为什么」比「怎么做」更重要,因为 API 会变,设计决策背后的取舍不会。

下一个分组进入 Hono 的实战层面——从中间件实战、数据库集成到部署上线,把这些原理知识用到真实项目里。

所属分组

03-Hono核心工作原理