H3 项目源码研读指南:从架构设计到贡献实践的完整地图
【免费下载链接】h3⚡️ Minimal H(TTP) framework built for high performance and portability项目地址: https://gitcode.com/GitHub_Trending/h31/h3
H3(读作 /eɪtʃθriː/)是一个以高性能与可移植性为设计目标的最小化 HTTP 框架,当前处于v2大版本——一次基于Web 标准原语(
Request、Response、URL、Headers)的彻底重写。本文以仓库根目录的 AGENTS.md 为骨架,结合 src、test、package.json 等源码与配置,系统讲解 H3 的核心架构、目录组织、代码规范、请求处理流程、测试方法论、构建链路与包导出设计,帮助你快速建立对仓库的全局认知,并具备直接参与贡献的能力。
快速参考:开发命令一览
AGENTS.md 给出了从环境搭建到基准测试的完整命令集,全部基于 pnpm 运行:
# 环境搭建 corepack enable && pnpm install # 开发 pnpm dev # vitest watch 模式(监听全部测试) pnpm vitest run <path> # 运行指定测试文件 pnpm test # 完整测试:lint + typecheck + coverage pnpm build # 使用 obuild 构建 pnpm lint # oxlint + oxfmt --check(lint + typecheck) pnpm fmt # automd + oxlint --fix + oxfmt pnpm bench:node # node 基准测试 pnpm bench:bun # bun 基准测试这些脚本在 package.json 中有精确定义:dev对应vitest,test是pnpm lint && pnpm typecheck && vitest --run --coverage的组合链,bench:node通过node --expose-gc --allow-natives-syntax运行 test/bench/bench.ts,bench:bun则直接以bun run执行同一文件。注意engines要求node >= 20.11.1,包管理器为pnpm@11.15.1。
核心架构设计
Web 标准优先
H3 v2 的底层不做任何自定义的请求/响应抽象,而是直接构建在 Web 标准之上:
- Web standards first:基于原生
Request、Response、URL、Headers,不发明私有协议对象; - Multi-runtime:同时支持 Node.js、Bun、Deno、Cloudflare Workers、Service Workers 与浏览器;
- Minimal core:生产依赖仅 2 个——
rou3(路由匹配引擎)与srvx(多运行时服务器抽象),见 package.json 的dependencies字段; - Handler-based:可组合的 handler + 中间件模式,不采用类继承重型的框架风格;
- Type-safe:全仓库启用严格 TypeScript,贯穿始终的泛型推断保证端到端类型安全。
sideEffects: false与"type": "module"的配置(package.json)进一步确认了这是一个纯 ESM、可被 tree-shaking 的现代库。
关键类
| 类 | 文件 | 用途 |
|---|---|---|
H3 | src/h3.ts | 主应用类(继承H3Core),补充路由方法(get/post/put/delete/...) |
H3Event | src/event.ts | 请求包装器——以惰性属性(URL、context)包装 WebRequest |
HTTPError | src/error.ts | 结构化 HTTP 错误,携带 status、data、headers |
HTTPResponse | src/response.ts | 灵活响应构建器 |
从源码看,H3与H3Core的分工非常清晰:H3Core(src/h3.ts#L41-L114)承载事件创建、fetch/handler调度、全局onRequest钩子触发与错误处理;H3(src/h3.ts#L152-L303)在其上叠加rou3路由表("~rou3")、on()/all()注册方法、use()/mount()中间件与子应用挂载,并通过一个循环为GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、CONNECT、TRACE、QUERY十个方法批量生成get()/post()等快捷方法。
H3Event(src/event.ts#L34-L120)则在构造时完成pathname的规范化解码:对/%61dmin这类“多余转义”做一次解码(%2F、%25除外,它们必须保持不透明),使路由匹配、use()匹配器与 handler 内读取event.url.pathname比较的是同一个字符串,杜绝编码绕过;而对/foo%、/%ZZ这类无规范形式的畸形编码,则标记kMalformedURL,在任意 handler 运行前以400拒绝,除非开启allowMalformedURL配置。
请求处理流程(Request Flow)
AGENTS.md 明确了七步请求生命周期,源码中每一步都有对应实现:
- 请求经平台适配器进入——各运行时入口位于 src/_entries(generic、node、bun、deno、cloudflare、service-worker);
H3.fetch()从Request创建H3Event——见 src/h3.ts#L60-L62,fetch委托给"~request",后者在 src/h3.ts#L73-L100 中new H3Event(request, context, app)并先检查畸形 URL;- 全局
onRequest钩子运行——config.onRequest在 src/h3.ts#L85-L93 中于路由分发前同步或异步执行; - 中间件链执行——按路由/方法匹配。
createDispatcher(src/h3.ts#L121-L138)是性能关键点:默认情况下中间件列表会被composeMiddleware预组合一次并缓存("~composed"),只在首次请求或use()/mount()使缓存失效后重建,避免每次请求的逐层分发开销; - 路由 handler 处理请求并返回值——
findRoute匹配时还会为 HEAD 请求回退到 GET 路由(RFC 9110,见 src/h3.ts#L254-L262); toResponse()将返回值转换为Response——自动处理 JSON、流、Blob 与原始类型,同时扁平化 Promise 链并在响应前运行onResponse钩子(src/response.ts#L13-L60);- 全局
onResponse钩子运行——作为终端的副作用钩子,即使抛错也不会逃逸生命周期(错误被捕获并视silent配置决定是否console.error)。
错误路径同样有保障:toError(src/response.ts#L73-L83)会把抛出的数字强制为状态码(throw 404)、把未知对象包装为带unhandled标记的500,且绝不信任非Error对象的结构来塑造响应——所有status/data/headers都被丢弃,只保留为cause,防止请求体回显等注入手段伪造响应描述符。
项目结构速览
src/ ├── index.ts # 公开 API 出口 ├── h3.ts # H3Core + H3 类 ├── event.ts # H3Event ├── handler.ts # defineHandler、defineValidatedHandler 等 ├── middleware.ts # 中间件系统 ├── response.ts # toResponse、HTTPResponse ├── error.ts # HTTPError ├── adapters.ts # Web/Node handler 适配器 ├── tracing.ts # Tracing 插件(独立入口) ├── types/ # 类型定义 │ ├── h3.ts # 应用类型(H3Config、H3Plugin、H3Route、HTTPMethod) │ ├── handler.ts # handler 类型(EventHandler、Middleware) │ ├── context.ts # H3EventContext │ ├── route-rules.ts # RouteRules(共享、可增强:合并到 event.context.routeRules) │ └── _utils.ts # 内部类型工具 ├── utils/ # 约 30 个工具模块(公开 API) │ ├── request.ts # getQuery、getRouterParams、getRequestURL 等 │ ├── response.ts # redirect、noContent、html、iterable 等 │ ├── body.ts # readBody、readValidatedBody、assertBodySize │ ├── cookie.ts # getCookie、setCookie、parseCookies、分块 cookie │ ├── session.ts # getSession、useSession、sealSession 等 │ ├── auth.ts # requireBasicAuth、basicAuth │ ├── cors.ts # handleCors、appendCorsHeaders 等 │ ├── proxy.ts # proxy、proxyRequest、fetchWithEvent │ ├── ws.ts # defineWebSocketHandler、defineWebSocket │ ├── json-rpc.ts # defineJsonRpcHandler、defineJsonRpcWebSocketHandler │ ├── event-stream.ts # createEventStream(SSE) │ ├── static.ts # serveStatic │ ├── cache.ts # handleCacheHeaders │ ├── middleware.ts # onRequest、onResponse、onError、bodyLimit │ ├── route.ts # defineRoute │ ├── base.ts # withBase │ └── internal/ # 内部辅助(不导出) │ ├── auth.ts、body.ts、cors.ts、encoding.ts 等 │ ├── iron-crypto.ts # Session 密封加密 │ ├── standard-schema.ts # 标准 schema 校验 │ └── validate.ts ├── rules/ # 路由规则(h3/rules 子路径入口) │ ├── index.ts # h3/rules — routeRules 中间件、匹配器、内置 handler │ ├── middleware.ts # routeRules() 即插即用中间件 │ ├── normalize.ts # normalizeRouteRules(配置 → 运行时规则) │ ├── match.ts # createRouteRulesMatcher、createMatcherFromFind、memoize │ ├── merge.ts # mergeMatchedRouteRules(层合并语义) │ ├── types.ts # RouteRuleConfig、NormalizedRouteRules 等 │ ├── cache.ts # h3/rules/cache — ocache 支撑的缓存 handler(可选 peer) │ ├── proxy.ts # h3/rules/proxy — proxyRequest 支撑的代理 handler │ ├── compiler.ts # h3/rules/compiler — 构建期 codegen │ ├── handlers/ # 内置规则 handler(headers、redirect、cors、cache) │ ├── compiler/ # Codegen 内部实现 │ └── internal/ # key 解析、作用域检查、node-key 分桶、预合并分析 ├── _entries/ # 平台特定入口 │ ├── generic.ts # Web Worker / 浏览器 │ ├── node.ts # Node.js(附加 toNodeHandler) │ ├── bun.ts # Bun │ ├── deno.ts # Deno │ ├── cloudflare.ts # Cloudflare Workers │ ├── service-worker.ts # Service Workers │ └── _common.ts # 共享入口工具 └── _deprecated.ts # 已废弃导出(v1 兼容) test/ ├── _setup.ts # 测试基础设施(describeMatrix、setupWebTest、setupNodeTest) ├── *.test.ts # 约 30 个集成测试文件 ├── rules/ # 路由规则测试(含类型测试 types.test-d.ts) ├── unit/ # 单元测试(含类型测试 types.test-d.ts) ├── bench/ # 基准测试(mitata) └── fixture/ # 运行时专用 playground 夹具公开 API 的完整清单以 src/index.ts 为准,它集中导出了路由、请求、响应、body、cookie、session、auth、cors、proxy、SSE、WebSocket、JSON-RPC、tracing、路由规则等全部工具函数与类型——这恰好印证了 AGENTS.md 中“utils 提供约 30 个公开工具模块”的说法。
代码规范(Code Conventions)
风格约束
- 仅 ESM——不引入 CommonJS;
- 所有导入路径显式携带
.ts扩展名; - 无 barrel 文件——直接从具体模块导入(src/index.ts 是唯一的公共聚合出口);
- 内部文件使用
_前缀(如_deprecated.ts、_entries/、_utils.ts); - 内部辅助函数放在文件末尾或
utils/internal/下; - 保持文件短小——单文件目标 < 200 行,超出即拆分;
- 多参数函数以第二个参数作为 options 对象;
- 格式化使用
oxfmt(零配置,采用默认值);lint 使用oxlint并启用unicorn、typescript、oxc插件。
命名约定
| 约定 | 含义 | 示例 |
|---|---|---|
k前缀 | 符号常量 | kNotFound、kHandled(见 src/response.ts#L10-L11) |
~前缀 | 私有/不可枚举属性 | "~middleware"、"~routes"、"~dispatch" |
#前缀 | 真正私有的类字段 | HTTPResponse的#headers、#init |
define*() | 工厂函数 | defineHandler、defineMiddleware、defineWebSocketHandler |
to*() | 转换函数 | toResponse、toEventHandler、toWebHandler |
from*() | 适配器函数 | fromWebHandler、fromNodeHandler |
这些命名贯穿全仓库:例如defineHandler(src/handler.ts#L23-L51)接受函数或对象(含handler/fetch/middleware字段)并合成带.fetch能力的 handler;toResponse(src/response.ts)负责将任意 handler 返回值收敛为Response;fromWebHandler/fromNodeHandler等适配器在 src/adapters.ts 中承担跨运行时桥接。
TypeScript 配置
- 严格模式 +
isolatedDeclarations+verbatimModuleSyntax; erasableSyntaxOnly: true——禁止 enum 与 namespace,保证类型可被无缝擦除;- Target/Module:
ESNext/NodeNext; - Lib:
["ESNext", "WebWorker", "DOM", "DOM.Iterable"]——这组 lib 正是“一套代码运行于 Node 与浏览器/Worker 两类环境”的类型学基础; - handler 中大量使用泛型做类型推断(
defineValidatedHandler等实验性 API 与utils/internal/standard-schema.ts的 Standard Schema 校验协作)。
响应处理模型
H3 采用handler 直接返回值而非res.send()模式,返回值到响应的映射规则如下:
- 返回
string→ 文本响应; - 返回
object→ JSON 响应; - 返回
Response/HTTPResponse→ 直接透传响应; - 返回
ReadableStream/Blob/File→ 流式响应; - 返回
kNotFound符号 →404; - 返回
kHandled符号 → 已处理完成(SSE、WebSocket 等场景)。
值得注意的细节:kNotFound与kHandled是Symbol.for注册的全局符号(src/response.ts#L10-L11),即使存在多份 h3 副本或跨 realm 也能稳定匹配。HTTPResponse的识别同样采用注册符号kHTTPResponse(src/response.ts#L94)而非constructor.name——注释明确指出 JSON 解析可伪造constructor属性,duck-typing 的 name 检查存在注入风险。
中间件遵循同一“返回值”哲学:normalizeMiddleware(src/middleware.ts#L14-L29)会把返回undefined或kNotFound的中间件视为“继续走next()”,让中间件既可以“短路响应”也可以“静默放行”。
测试体系
框架与矩阵测试
- Vitestv4+(见 package.json 的 devDependencies)配合v8覆盖率(
@vitest/coverage-v8); - 矩阵测试:每个测试都在
web与node两种模式下各跑一遍。
describeMatrix的实现见 test/_setup.ts#L11-L38:它在内部生成web、node两个describe分组,分别调用setupWebTest(走ctx.app.request(...)纯内存路径)与setupNodeTest(起真实node:http服务器 +toNodeHandler适配)。web 模式会自动补Host: localhost头,node 模式还会模拟反向代理场景补Host。
编写测试
import { describeMatrix } from "./_setup.ts"; describeMatrix("feature name", (ctx, { it, expect }) => { it("does something", async () => { ctx.app.get("/test", () => "hello"); const res = await ctx.fetch("/test"); expect(await res.text()).toBe("hello"); }); });关键模式:
- 使用
describeMatrix编写跨运行时测试; ctx.app是每个测试独立的全新H3实例(由beforeEach保证);ctx.fetch统一处理 web/node 两种模式的 URL 解析;ctx.errors追踪未处理错误(afterEach中自动断言);- 用
it.skipIf(ctx.target === "node")跳过运行时特定用例。
运行测试
pnpm vitest run test/body.test.ts # 单文件 pnpm vitest run test/unit/ # 单元测试 pnpm dev # watch 模式(全部) pnpm test # 完整:lint + typecheck + coverageBug 修复工作流
- 编写可复现 bug 的回归测试;
- 在改动任何代码前确认测试失败;
- 以最小改动修复实现;
- 确认测试通过;
- 运行更广的测试集做回归检查。
这套 TDD 式流程与仓库中的测试组织一一对应:集成测试平铺在 test 根目录(约 30 个*.test.ts),规则子系统在 test/rules,单元测试与类型测试在 test/unit,基准在 test/bench。
构建与包导出
构建链路
- 使用obuild(基于 Rolldown 打包器,见 package.json 的
"build": "obuild"脚本); - 6 个平台入口+
tracing.ts+ 4 个rules/*入口分别作为独立 entry 构建; - 开启code splitting(产出
h3-[hash].mjs分块); - 自定义插件剥离注释(保留
#/@注解); - 产物:
dist/_entries/*.mjs+dist/*.d.mts。
包导出映射
h3 → 按运行时自动解析(deno/bun/workerd/node/default) h3/node → Node.js 运行时(附加 toNodeHandler) h3/bun → Bun 运行时 h3/deno → Deno 运行时 h3/cloudflare → Cloudflare Workers h3/service-worker → Service Workers h3/generic → 通用 Web 标准 h3/tracing → Tracing 插件 h3/rules → 路由规则(routeRules 中间件、匹配器、内置 handler) h3/rules/cache → ocache 支撑的 cache 规则 handler(可选 ocache peer) h3/rules/proxy → proxy 规则 handler(引入 proxyRequest) h3/rules/compiler → 构建期路由规则 codegen上述导出与 package.json#L18-L39 的exports字段完全一致:根入口"."使用deno/bun/workerd/browser/node/default条件导出按运行时解析到 dist/_entries 下对应的入口文件。h3/rules各子路径则对应该系统独立构建的产物。
依赖一览
| 依赖 | 用途 |
|---|---|
rou3 | 路由匹配引擎(^0.9.2) |
srvx | 服务器抽象层,多运行时支持(^0.12.7) |
crossws | WebSocket 抽象(可选 peer 依赖) |
ocache | 为h3/rules/cache提供响应缓存(可选 peer 依赖) |
package.json#L101-L112 证实crossws与ocache均为optional: true的 peerDependencies——即核心零负担,仅在需要 WebSocket 或规则缓存能力时由使用者自行安装,这再次呼应了“minimal core”的设计哲学。
贡献最佳实践
AGENTS.md 最后给出了面向贡献者的五条准则:
- 优先使用 Web 标准 API 而非运行时特定 API;
- 保持核心最小化——新增工具函数,而不是为核心增加复杂度;
- 使用
describeMatrix跨运行时测试; - handler 返回值而非修改响应对象;
- 使用
defineHandler/defineMiddleware获得类型安全。
配套的 examples 目录提供了大量可运行示例,例如 examples/middleware.mjs 展示了new H3()、链式.get()注册路由、onRequest/onResponse/onError全局钩子的组合用法,是理解“返回值哲学”与钩子语义最直接的入口。
结语
AGENTS.md 是一份面向 Agent 与人类开发者的“仓库宪法”:它浓缩了 H3 v2 的架构决策(Web 标准优先、多运行时、最小核心、handler 组合)、工程化约定(命名、ESM、类型擦除、短文件)与质量红线(矩阵测试、回归优先、树摇友好)。当你阅读本文并对照 src、test 与 package.json 逐一验证时,会发现自己已经掌握了这个仓库从请求进入、路由匹配、中间件组合、响应序列化,到测试与构建交付的完整闭环——这正是参与 H3 贡献前需要建立的心智模型。
【免费下载链接】h3⚡️ Minimal H(TTP) framework built for high performance and portability项目地址: https://gitcode.com/GitHub_Trending/h31/h3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考