H3 v1 到 v2 迁移:弃用清单
8.1 讲了四个核心变化,这一节是逐项的弃用对照表。源文档给出的弃用项很多,按「请求体 / Cookie 与 Headers / 工具函数 / Node 与 Web 适配 / 类型导出」五组归并。每组都是三列:v1 写法 → v2 写法 → 备注。
💡 一个好消息先说在前面:v2 仍然导出所有被弃用的 v1 名称,所以存量代码不会一升级就编译报错——这些名字只是打上了弃用标记,会逐步移除。真正的破坏性来自 8.1 讲的「模型级变化」(send→return、router 合并),而不是这张表。
四个废弃的 H3Event 属性
先列最隐蔽的一类:下面四个属性在 v2 被标记为废弃(未来版本可能移除)。它们现在还能用,但应尽早换成新的取值路径,因为拿到的对象形态已经变了:
| v1 属性 | v2 取值 | 备注 |
|---|---|---|
event.path |
event.url.pathname + event.url.search |
不再单独返回路径,需自己拼 query |
event.method |
event.req.method |
改从标准 Request 读 |
event.headers |
event.req.headers |
现在是标准 Headers |
event.node |
event.runtime.node |
仅 Node 运行时存在,挪了位置 |
最容易踩的坑是 event.path:v1 只返回路径本身,v2 建议用 event.url.pathname(注意不含 query string)。如果你代码里既用 event.path 又自己另取 query,迁移后直接换成 event.url 上的两个字段,语义更清晰。
一、请求体工具
v2 的原则是「请求体优先用原生 event.req.*」,旧的 read* 包装大多有了标准替代;同时 readBody 不再用 destr 自动反序列化(见 6.2/7.7,这关乎原型污染,必须自己过滤)。
| v1 写法 | v2 写法 | 备注 |
|---|---|---|
readRawBody(event) |
event.req.text() / event.req.arrayBuffer() |
直接读原始文本或二进制 |
getBodyStream(event) |
event.req.body |
原生 ReadableStream |
getRequestWebStream(event) |
event.req.body |
同上 |
readFormData(event) |
event.req.formData() |
标准 FormData |
readMultipartFormData(event) |
event.req.formData() |
同上,含文件 |
readFormDataBody(event) |
event.req.formData() |
同上 |
二、Cookie 与 Headers
v2 全面改用标准 Web Headers:Header 值现在永远是普通 string,不再可能是 null / undefined / number / string[]——这是类型上的破坏性变化,v1 里判断「header 是否存在」时写 if (header) 还能兼容 falsy,v2 里要改成 if (header !== null) 或直接用 headers.has(name)。
Set-Cookie 因为标准 Headers 只留一个字符串,改用 headers.getSetCookie(),它始终返回字符串数组(每个 cookie 一项),多 cookie 场景下比 v1 的拼接字符串好处理得多。
// v1:Set-Cookie 是拼接字符串,要自己拆分
const cookies = getResponseHeader(event, "set-cookie")?.split(", ");
// v2:getSetCookie 直接给数组
const cookies = event.res.headers.getSetCookie();
⚠️
getRequestProtocol(以及getRequestURL)不再默认信任x-forwarded-proto头。 这是安全相关的关键变化:v2 必须显式opt in才信任它——getRequestProtocol(event, { xForwardedProto: true })。这与getRequestHost(xForwardedHost)和getRequestIP(xForwardedFor)既有的 opt-in 行为一致。只在你确定前面有可信的反向代理或 CDN 时再开,否则客户端可以伪造协议头、绕过你的 HTTPS 强制逻辑。
三、工具函数
这一组最大,再按用途拆成六个子表。
应用与路由工具
| v1 写法 | v2 写法 | 备注 |
|---|---|---|
createApp / createRouter |
new H3() |
两者合并,见 8.1 |
错误工具
| v1 写法 | v2 写法 | 备注 |
|---|---|---|
createError / H3Error |
HTTPError |
统一错误类型 |
isError(error) |
HTTPError.isError(error) |
改为类型上的静态方法 |
Handler 工具
| v1 写法 | v2 写法 | 备注 |
|---|---|---|
eventHandler / defineEventHandler |
defineHandler(或直接用函数) |
命名更短,函数本身即可作 handler |
lazyEventHandler |
defineLazyEventHandler |
仅改名字 |
isEventHandler |
(已移除) | 任意函数都可以是 handler,不再需要判断 |
useBase |
withBase |
前缀应用工具改名 |
defineRequestMiddleware / defineResponseMiddleware |
(已移除) | v2 用 (event, next) 中间件或全局钩子替代 |
请求工具
| v1 写法 | v2 写法 | 备注 |
|---|---|---|
getHeader(event, name) / getRequestHeader(event, name) |
event.req.headers.get(name) |
直接读标准 Headers |
getHeaders(event) / getRequestHeaders(event) |
Object.fromEntries(event.req.headers.entries()) |
全量转对象 |
getRequestPath(event) |
event.url.pathname |
改用 URL.pathname |
getMethod(event) |
event.req.method |
改用标准 Request |
getRequestProtocol(event) |
event.req 取协议 + { xForwardedProto: true } 才信转发头 |
见本节上方警告 |
响应工具
| v1 写法 | v2 写法 | 备注 |
|---|---|---|
getResponseHeader / getResponseHeaders |
event.res.headers.get(name) |
直接读标准 Headers |
setHeader / setResponseHeader / setHeaders / setResponseHeaders |
event.res.headers.set(name, value) |
统一到 set |
appendHeader / appendResponseHeader / appendResponseHeaders |
event.res.headers.append(name, value) |
统一到 append |
removeResponseHeader / clearResponseHeaders |
event.res.headers.delete(name) |
统一到 delete |
appendHeaders |
appendResponseHeaders |
别名收敛 |
defaultContentType |
event.res.headers.set("content-type", type) |
不再有专门函数 |
getResponseStatus / getResponseStatusText / setResponseStatus |
event.res.status / event.res.statusText |
改用标准 Response 字段 |
其他工具
| v1 写法 | v2 写法 | 备注 |
|---|---|---|
createEventStream(event) |
new EventStream(event) |
用 class 构造,见 5.2 |
isStream(value) |
value instanceof ReadableStream |
不再有判断函数 |
isWebResponse(value) |
value instanceof Response |
同上 |
splitCookiesString |
用 cookie-es 的 splitSetCookieString |
移出核心,转第三方 |
MIMES |
(已移除) | 不再内置 MIME 映射 |
四、Node 与 Web 适配
v2 把适配函数统一成「Handler / Request / Response」命名,并移除了一批一次性转换工具(因为它们已被 Web API 原生取代)。
| v1 写法 | v2 写法 | 备注 |
|---|---|---|
defineNodeListener |
defineNodeHandler |
仅改名字 |
fromNodeMiddleware |
fromNodeHandler |
仅改名字 |
toNodeListener |
toNodeHandler |
仅改名字 |
createEvent(event) |
(已移除) | 改用 Node 适配器 toNodeHandler(app) |
fromNodeRequest |
(已移除) | 同上 |
promisifyNodeListener |
(已移除) | —— |
callNodeListener |
(已移除) | —— |
fromPlainHandler |
(已移除) | 直接改用 Web API |
toPlainHandler |
(已移除) | 直接改用 Web API |
fromPlainRequest |
(已移除) | 改用 Web API 或 mockEvent 测试 |
callWithPlainRequest |
(已移除) | 直接改用 Web API |
fromWebRequest |
(已移除) | 直接改用 Web API |
callWithWebRequest |
(已移除) | —— |
这一组移除项很多,但逻辑很一致:v2 本身就是 Web 标准的,那些"把 H3 事件 ↔ 平台原语"反复转来转去的桥接函数不再需要——你直接操作
Request/Response即可。唯一保留的适配层是 Node 的toNodeHandler,因为 Node 生态仍需一个入口。
五、类型导出
类型名几乎全改了,目标是对齐 Web 标准命名。下面是改名映射:
| v1 类型 | v2 类型 | 备注 |
|---|---|---|
App |
H3 |
对齐新实例名 |
AppOptions |
H3Config |
—— |
_RequestMiddleware |
RequestMiddleware |
去掉下划线前缀 |
_ResponseMiddleware |
ResponseMiddleware |
同上 |
NodeListener |
NodeHandler |
命名对齐 |
TypedHeaders |
RequestHeaders / ResponseHeaders |
按请求/响应拆开 |
HTTPHeaderName |
RequestHeaderName / ResponseHeaderName |
同上 |
H3Headers |
原生 Headers |
不再有包装类型 |
H3Response |
原生 Response |
同上 |
MultiPartData |
原生 FormData |
同上 |
RouteNode |
RouterEntry |
路由条目 |
CreateRouterOptions |
RouterOptions |
—— |
已删除(不再导出的)类型:WebEventContext、NodeEventContext、NodePromisifiedHandler、AppUse、Stack、InputLayer、InputStack、Layer、Matcher、PlainHandler、PlainRequest、PlainResponse、WebHandler。
如果你的代码里 import type 了这些名字,升级后 Typescript 会报「找不到名称」——对照上表改名字(或删掉已删除的)即可。
把这五组对照表与 8.1 的四点核心变化合起来,就是 v1→v2 的完整迁移面。下一节(8.3)回到 v2 正式版的官方发布说明,从「为什么值得升级」的角度再梳理一遍。