codecamp

H3 是什么

写 Node.js 接口有很多选择,但多数选择都会先教你一套它自己的概念:上下文对象、洋葱模型、插件注册表、req 与 res 的私有扩展。等你换一个运行时——从 Node 换到 Bun、Deno 或 Cloudflare Workers——这些概念大半要重学一遍。

H3 走的是另一条路:它直接建立在 Web 标准之上,请求就是 Request,响应就是 Response,URL 就是 URL,头部就是 Headers。你在浏览器里见过的那些对象,在服务端原样复用。这是理解 H3 全部设计的第一把钥匙。

📌 本文对应 H3 v2 文档。v1 的旧文档仍在 v1.h3.dev,两者 API 差异较大,v1 写法不要直接搬进 v2 项目(完整差异见 8.1)。

一句话定位

H3(读作 /eɪtʃθriː/,即字母 H 加 3)是一个轻量、快速、可组合的服务端框架,面向现代 JavaScript 运行时。它不提供一个大而全的核心,而是给你一个极小的 H3 实例,再让你按需引入工具函数。

它与 unjs 生态同源——后面会反复出现的 srvx、rou3 都在同一套工具链里;Nitro、Nuxt 这类上层框架的服务端也建在它之上。你即使没直接用过 H3,也可能已经在间接使用它。

四个 Web 标准原语

H3 的整个请求-响应模型可以用四个标准对象概括,对应关系如下:

Web 标准对象 H3 中的位置 用途
Request event.req 请求方法、头部、请求体
Response event.res 响应状态、响应头(返回体用 return)
URL event.url 路径、查询参数
Headers event.req.headers / event.res.headers 头部读写

这意味着两件事:查 MDN 就能查到 H3 的一半 API;换运行时不需要换心智模型——差异只在于底层由谁把操作系统的套接字接到这些标准对象上。

⚠️ 一处容易踩的坑:v1 里的 event.web 在 v2 已改名为 event.req;event.node 也改成了 event.runtime.node,且只在 Node 运行时存在。

为什么是「可组合」而不是「大核心」

官方把可组合性列为 H3 的核心取舍,理由有三条,值得逐条理解:

  1. 只打包用到的代码。 工具函数是独立模块,import 了才进产物,没用到的不会进服务器。
  2. 体积随需求增长,而不是随框架增长。 用法显式,不会因为引入框架就带上几十个用不到的中间件。
  3. 对全局的影响极小。 H3 几乎不往全局注入东西,你引入一个工具不会改变别处的行为。

代价也要说清楚:没有「开箱即用的全家桶」。数据库、模板、鉴权、日志都得自己接,或者找生态里的包。如果你要的是「一条命令生成完整后端」,H3 不是那个答案;如果你要的是「一层薄而可控的 HTTP 层」,它正合适。

最小可运行形态

先看一眼它长什么样,后面再逐行拆:

// 从 h3 引入应用类与服务启动函数
import { H3, serve } from "h3";

// new H3() 创建一个应用实例,.get() 注册一条 GET 路由
// 直接把字符串返回出去,H3 会自动转成 HTTP 响应
const app = new H3().get("/", () => "⚡️ 你好,H3!");

// serve 启动监听,底层由 srvx 适配到当前运行时
serve(app, { port: 3000 });

这里已经体现了两条与 Express 系最大的不同:

  • 返回值即响应。 不需要 res.send(),return 一个字符串、对象、流或 Response 都可以,H3 自己判断怎么发。
  • 启动与框架解耦。 serve() 来自 srvx,同一份代码在 Node、Bun、Deno 上都能跑,不需要为每个运行时写不同的启动代码。

和 Express、Koa 摆在一起看

维度 Express Koa H3 v2
底层对象 自有的 req / res 扩展 自有的 ctx 标准 Request / Response
响应方式 res.send() / res.json() 赋值 ctx.body return 值或 throw 错误
中间件模型 线性注册表 洋葱模型 返回响应即短路,可组合
跨运行时 基本只面向 Node 基本只面向 Node Node / Bun / Deno / Workers

这不是「谁更好」的排名,而是模型差异:Express 与 Koa 在 Node 上深耕多年,生态与中间件更成熟;H3 把赌注押在「Web 标准会成为服务端的通用底座」上。

这本教程怎么读

全书 8 章 39 节,按三个层次组织,可以按目标直接跳读:

你的目标 建议路线
先把它跑起来,能写几个接口 第 1 章(安装与第一个服务)→ 第 2 章(生命周期、路由、中间件、响应、错误)
用配置替代重复中间件 第 3 章(路由规则:响应头、重定向、跨域、缓存、代理)
照着示例做一个完整功能 第 7 章(Cookie、QUERY 方法、会话、静态资源、流式响应、数据校验)
把 v1 老项目升级上来 8.1 核心变更 → 8.2 弃用清单

💡 阅读顺序上的一个建议:第 2 章的「请求生命周期」虽然是概念页,但它解释了「中间件不返回会发生什么」「handler 不返回为什么会得到 200」,先读它能省掉后面大量困惑。

来源与许可

本文档译自 H3 官方文档(h3.dev)与 h3js/h3 仓库,基线版本 v2.0.8(2026-10-22 发布)。官方文档以 MIT License 发布,Copyright (c) Pooya Parsa,代码示例版权归原作者所有;本教程的翻译、注释与本土化补充由编程狮整理。

H3 安装与第一个服务
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }