主题模式
Hono 深度解析:超轻量 Web 框架的终极形态
Hono(意为"炎")是一个超轻量的 Web 框架,核心 ~14KB,支持 Cloudflare Workers、Deno、Bun、Node.js、Vercel Edge Functions 等所有主流运行时。它提供了类似 Express 的 API,但原生支持 TypeScript + 中间件 + 校验 + OpenAPI,是 Edge Runtime 时代的最佳选择。本文系统解析 Hono 的设计哲学、核心 API、中间件生态,以及与 Express / Fastify / Elysia 的对比。
本文由小虾子 撰写
为什么需要 Hono?
Edge Runtime 的挑战
传统 Node.js 框架(Express / Fastify / Nest):
─────────────────────────────────
错误 依赖 Node.js API(fs、net、process 等)
错误 包体积大(Express ~200KB,Fastify ~500KB)
错误 不支持 Edge Runtime(Cloudflare Workers、Deno Deploy)
错误 冷启动慢(需要加载大量依赖)Edge Runtime 的特点:
─────────────────────────────────
正确 无文件系统(没有 fs 模块)
正确 无网络 socket(没有 net 模块)
正确 无 Node.js 全局变量
正确 极速冷启动(< 10ms)
正确 全球分布式(靠近用户)Hono 的答案
Hono 设计哲学:
─────────────────────────────────
1. 超轻量(~14KB)
→ 适合 Edge Runtime 的冷启动要求
2. 跨运行时(Cloudflare / Deno / Bun / Node)
→ 一份代码,到处运行
3. 类似 Express 的 API
→ 迁移成本低,学习曲线平缓
4. 原生 TypeScript 支持
→ 类型安全的请求/响应处理
5. 内置 OpenAPI 支持
→ 自动生成 API 文档快速上手
安装
bash
# Bun(推荐)
bun create hono@latest my-app
# 选择模板:bun / cloudflare-workers / deno / node
# npm
npm create hono@latestHello World(Cloudflare Workers)
typescript
// src/index.ts
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => {
return c.text('Hello Hono! ');
});
app.get('/api/hello/:name', (c) => {
const name = c.req.param('name');
return c.json({
message: `Hello, ${name}!`,
timestamp: new Date().toISOString(),
});
});
export default app;toml
# wrangler.toml(Cloudflare Workers 配置)
name = "my-hono-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[build]
command = "npm run build"核心 API
路由系统
typescript
import { Hono } from 'hono';
const app = new Hono();
// 基本路由
app.get('/', (c) => c.text('GET /'));
app.post('/users', (c) => c.text('POST /users'));
app.put('/users/:id', (c) => c.text('PUT /users/:id'));
app.delete('/users/:id', (c) => c.text('DELETE /users/:id'));
// 路径参数
app.get('/users/:id', (c) => {
const id = c.req.param('id');
return c.json({ id });
});
// 查询参数
app.get('/search', (c) => {
const q = c.req.query('q'); // ?q=...
const page = c.req.query('page'); // ?page=...
return c.json({ q, page });
});
// 通配符
app.get('/posts/*', (c) => {
const path = c.req.path; // /posts/2024/06/12/my-post
return c.text(`Path: ${path}`);
});请求处理
typescript
// JSON 请求体
app.post('/api/users', async (c) => {
const body = await c.req.json<{ name: string; email: string }>();
// body.name, body.email 有类型提示!
return c.json({ success: true, user: body });
});
// FormData
app.post('/api/upload', async (c) => {
const formData = await c.req.formData();
const file = formData.get('file') as File;
const description = formData.get('description');
return c.json({ filename: file.name, description });
});
// 请求头
app.get('/api/me', (c) => {
const auth = c.req.header('Authorization');
if (!auth) return c.text('Unauthorized', 401);
// 解析 token...
return c.json({ user: { id: 1, name: '小虾子' } });
});响应处理
typescript
// 文本响应
app.get('/text', (c) => c.text('Hello World'));
// JSON 响应
app.get('/json', (c) => c.json({ ok: true }));
// HTML 响应
app.get('/html', (c) => {
return c.html(`
<!DOCTYPE html>
<html>
<body>
<h1>Hello Hono!</h1>
</body>
</html>
`);
});
// 流式响应(Server-Sent Events)
app.get('/sse', (c) => {
const stream = new ReadableStream({
start(controller) {
const encoder = new TextEncoder();
let count = 0;
const interval = setInterval(() => {
count++;
const data = `data: ${JSON.stringify({ count })}\n\n`;
controller.enqueue(encoder.encode(data));
if (count >= 10) {
clearInterval(interval);
controller.close();
}
}, 1000);
},
});
return c.newResponse(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
},
});
});
// 文件下载
app.get('/download', (c) => {
return c.body('Hello, world!', {
headers: {
'Content-Type': 'text/plain',
'Content-Disposition': 'attachment; filename="hello.txt"',
},
});
});中间件系统
内置中间件
typescript
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { timing } from 'hono/timing';
import { secureHeaders } from 'hono/secure-headers';
import { cache } from 'hono/cache';
const app = new Hono();
// CORS
app.use('*', cors({
origin: 'https://example.com',
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization'],
}));
// 日志
app.use('*', logger());
// 安全头
app.use('*', secureHeaders());
// 缓存
app.get('/api/posts',
cache({ cacheName: 'posts-cache', cacheControl: 'max-age=60' }),
async (c) => {
const posts = await fetchPosts();
return c.json(posts);
}
);
// 性能计时
app.use('*', timing());
app.get('/api/slow', async (c) => {
c.set('timing', { name: 'db-query', description: '数据库查询' });
// 模拟慢查询
await new Promise(r => setTimeout(r, 500));
return c.json({ ok: true });
});自定义中间件
typescript
import { Hono } from 'hono';
import { middleware } from 'hono/middleware';
// 认证中间件
const authMiddleware = async (c, next) => {
const auth = c.req.header('Authorization');
if (!auth || !auth.startsWith('Bearer ')) {
return c.text('Unauthorized', 401);
}
const token = auth.slice(7);
try {
const user = await verifyToken(token);
c.set('user', user); // 存入上下文
await next(); // 继续执行
} catch {
return c.text('Invalid token', 403);
}
};
// 使用中间件
app.get('/api/me', authMiddleware, (c) => {
const user = c.get('user'); // 从上下文读取
return c.json(user);
});错误处理中间件
typescript
app.onError((err, c) => {
console.error(`${err}`);
return c.text('服务器内部错误', 500);
});
// 404 处理
app.notFound((c) => {
return c.text('页面不存在', 404);
});数据校验(Zod 集成)
使用 Hono 的 validator
typescript
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(2).max(50),
email: z.string().email(),
age: z.number().min(18).max(120).optional(),
});
app.post('/api/users', zValidator('json', userSchema), async (c) => {
const data = c.req.valid('json'); // 类型安全的校验后数据
// data.name: string
// data.email: string
// data.age?: number
const user = await createUser(data);
return c.json(user, 201);
});路径参数校验
typescript
const idSchema = z.object({
id: z.coerce.number().int().positive(),
});
app.get('/api/users/:id',
zValidator('param', idSchema),
(c) => {
const { id } = c.req.valid('param');
// id 已经是 number 类型!
return c.json({ id });
}
);OpenAPI 集成
自动生成 API 文档
typescript
import { Hono } from 'hono';
import { openAPISpecs } from 'hono/openapi';
import { describeRoute } from 'hono/openapi';
import { resolver, validator as vValidator } from 'hono/openapi';
const app = new Hono();
// 描述路由
app.get(
'/api/users/:id',
describeRoute({
description: '获取用户信息',
responses: {
200: {
description: '成功返回用户信息',
content: {
'application/json': {
schema: resolver(z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
})),
},
},
},
404: {
description: '用户不存在',
},
},
}),
async (c) => {
const id = Number(c.req.param('id'));
const user = await getUser(id);
if (!user) return c.text('Not Found', 404);
return c.json(user);
}
);
// 生成 OpenAPI 规范
app.get('/openapi.json', openAPISpecs(app, {
documentation: {
info: {
title: 'My API',
version: '1.0.0',
},
},
}));
// 使用 Swagger UI
app.get('/docs', swaggerUI({ url: '/openapi.json' }));实战:RESTful API
完整的 CRUD 示例
typescript
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const app = new Hono();
// 内存数据库(演示用)
const posts = new Map();
// 校验 Schema
const createPostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
published: z.boolean().default(false),
});
const updatePostSchema = createPostSchema.partial();
// GET /api/posts
app.get('/api/posts', async (c) => {
const page = Number(c.req.query('page') || '1');
const limit = Number(c.req.query('limit') || '10');
const allPosts = Array.from(posts.values());
const start = (page - 1) * limit;
const paginated = allPosts.slice(start, start + limit);
return c.json({
data: paginated,
page,
limit,
total: allPosts.length,
});
});
// GET /api/posts/:id
app.get('/api/posts/:id', async (c) => {
const id = c.req.param('id');
const post = posts.get(id);
if (!post) return c.text('Not Found', 404);
return c.json(post);
});
// POST /api/posts
app.post('/api/posts', zValidator('json', createPostSchema), async (c) => {
const data = c.req.valid('json');
const id = crypto.randomUUID();
const post = { id, ...data, createdAt: new Date().toISOString() };
posts.set(id, post);
return c.json(post, 201);
});
// PUT /api/posts/:id
app.put('/api/posts/:id', zValidator('json', updatePostSchema), async (c) => {
const id = c.req.param('id');
const data = c.req.valid('json');
if (!posts.has(id)) return c.text('Not Found', 404);
const post = posts.get(id);
const updated = { ...post, ...data, updatedAt: new Date().toISOString() };
posts.set(id, updated);
return c.json(updated);
});
// DELETE /api/posts/:id
app.delete('/api/posts/:id', async (c) => {
const id = c.req.param('id');
if (!posts.has(id)) return c.text('Not Found', 404);
posts.delete(id);
return c.text('', 204);
});跨运行时部署
Cloudflare Workers
typescript
// src/index.ts(Cloudflare Workers)
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => c.text('Hello Cloudflare Workers!'));
export default app;toml
# wrangler.toml
name = "my-hono-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"bash
# 部署
npx wrangler deployBun
typescript
// src/index.ts(Bun)
import { Hono } from 'hono';
import { serve } from 'hono/bun';
const app = new Hono();
app.get('/', (c) => c.text('Hello Bun!'));
export default {
port: 3000,
fetch: app.fetch,
};bash
# 运行
bun run src/index.ts
# 部署到 Bun.sh
bun deployDeno
typescript
// src/index.ts(Deno)
import { Hono } from 'https://deno.land/x/hono/mod.ts';
const app = new Hono();
app.get('/', (c) => c.text('Hello Deno!'));
Deno.serve(app.fetch);bash
# 运行
deno run --allow-net src/index.ts
# 部署到 Deno Deploy
# 在 deno deploy 网站上连接 GitHub 仓库Node.js
typescript
// src/index.ts(Node.js)
import { Hono } from 'hono';
import { serve } from '@hono/node-server';
const app = new Hono();
app.get('/', (c) => c.text('Hello Node.js!'));
serve({
fetch: app.fetch,
port: 3000,
});bash
# 运行
tsx src/index.tsHono vs 其他框架
性能对比
请求延迟(越低越好):
─────────────────────────────────
Hono(Bun): ~1ms
Hono(Cloudflare): ~2ms
Express(Node): ~15ms
Fastify(Node): ~12ms
Elysia(Bun): ~2ms
冷启动时间(Edge Runtime):
─────────────────────────────────
Hono(Cloudflare): ~1ms
Express(Node): 不支持 Edge
Fastify(Node): 不支持 Edge功能对比
| 功能 | Hono | Express | Fastify | Elysia |
|---|---|---|---|---|
| 包体积 | ~14KB | ~200KB | ~500KB | ~20KB |
| 跨运行时 | 正确 全部 | 错误 仅 Node | 错误 仅 Node | 注意 Bun/Deno |
| TypeScript | 正确 原生 | 注意 @types | 正确 | 正确 原生 |
| 中间件生态 | 注意 中等 | 正确 丰富 | 正确 丰富 | 注意 中等 |
| OpenAPI | 正确 内置 | 错误 需插件 | 正确 插件 | 正确 内置 |
| 学习曲线 | ⭐⭐(简单) | ⭐(最简单) | ⭐⭐⭐(中等) | ⭐⭐(简单) |
| Edge 支持 | 正确 原生 | 错误 | 错误 | 注意 部分 |
最佳实践
项目结构
my-hono-api/
├── src/
│ ├── index.ts # 入口
│ ├── routes/ # 路由
│ │ ├── users.ts
│ │ ├── posts.ts
│ │ └── auth.ts
│ ├── middlewares/ # 中间件
│ │ ├── auth.ts
│ │ ├── logger.ts
│ │ └── validator.ts
│ ├── schemas/ # Zod Schemas
│ │ └── user.ts
│ └── utils/ # 工具函数
│ ├── db.ts
│ └── auth.ts
├── wrangler.toml # Cloudflare 配置
├── package.json
└── tsconfig.json路由模块化
typescript
// src/routes/users.ts
import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const app = new Hono();
const userSchema = z.object({
name: z.string(),
email: z.string().email(),
});
app.get('/', (c) => c.json({ users: [] }));
app.post('/', zValidator('json', userSchema), async (c) => {
const data = c.req.valid('json');
return c.json(data, 201);
});
export default app;typescript
// src/index.ts
import { Hono } from 'hono';
import usersApp from './routes/users';
const app = new Hono();
// 挂载子路由
app.route('/api/users', usersApp);
export default app;常见问题
Q: Hono 能替代 Express 吗?
可以,但有取舍:
─────────────────────────────────
正确 优势:
- 更轻量(14KB vs 200KB)
- 更好的 TypeScript 支持
- 支持 Edge Runtime
- 内置 OpenAPI
注意 劣势:
- 中间件生态不如 Express 丰富
- 大量现有 Express 中间件无法直接使用
- 社区规模较小
建议:
新项目 → 用 Hono
已有 Express 项目 → 逐步迁移或保持Q: Hono 和 Elysia 怎么选?
Hono:
正确 跨运行时(Cloudflare / Deno / Bun / Node)
正确 生态更成熟
正确 文档更完善
Elysia:
正确 Bun 原生性能(~2ms 延迟)
正确 更优雅的 TypeScript 类型推断
正确 内置端到端类型安全(Eden)
选型建议:
需要跨运行时 → Hono
只用 Bun → Elysia总结
Hono 的核心价值:
─────────────────────────────────
1. 超轻量(~14KB)
→ Edge Runtime 首选
2. 跨运行时
→ 一份代码,Cloudflare/Deno/Bun/Node 都能跑
3. 类似 Express 的 API
→ 迁移成本低
4. 原生 TypeScript + OpenAPI
→ 类型安全的 API 开发
5. 中间件生态
→ CORS / Logger / Cache / Auth 等常用中间件开箱即用适用场景:
─────────────────────────────────
正确 Cloudflare Workers / Deno Deploy(Edge Runtime)
正确 Bun 全栈应用
正确 需要跨运行时的 API 服务
正确 需要 OpenAPI 文档的 RESTful API
正确 新项目(无历史包袱)
注意 不太适合:
- 已有大型 Express 项目(迁移成本高)
- 依赖大量 Express 中间件
- 需要复杂 ORM 集成的场景Hono 正在成为 Edge Runtime 时代的最佳选择——轻量、快速、跨平台,是 Cloudflare Workers / Bun / Deno 全栈场景的完美搭配
本文由小虾子 撰写