H3 v1.8 回顾

2026-10-30 17:48 更新

⚠️ 这是历史版本内容,请勿在新项目里照抄。 本节回顾的是 H3 v1.8(2023-08-15 发布),文中所有 API 都是 v1 旧写法。把它当作「v2 设计思路从哪来」的背景章即可——v2 的 Web 标准兼容、流式响应、对象式 handler、类型化请求等基因,正是在 v1.8 这次发布里埋下的。新项目请一律用 v2(见 8.1–8.3)。

H3 是用 TypeScript 写的轻量 HTTP 框架,至今支撑着 Nitro 和 Nuxt。v1.8 的目标是把 H3「进一步拉到 Web API 兼容的边上」——这也是后来 v2 全面 Web 标准重写的直接前奏。

Web 与 Plain 适配器

v1.8 引入了一个 fetch 兼容的适配器:输入是标准 Request、返回标准 Response。这让 H3 应用能直接跑在 Cloudflare Workers、Deno Deploy、Bun、Lagon 这类边缘运行时。同时新增了 toPlainHandler,用普通输入输出对象对接任意 Serverless 平台。

// v1.8 写法(历史):用 esm.sh 引入,部署到边缘
import { createApp, eventHandler, toWebHandler } from "https://esm.sh/h3@1.8.0";

const app = createApp();
app.use("/", eventHandler((event) => "H3 works on edge!"));

// 转成 (Request) => Promise<Response>,边缘运行时直接接
const webHandler = toWebHandler(app);

这次还带来一批 Web 辅助函数:toWebRequest(event)、getRequestWebStream(event)、fromPlainHandler(plainHandler)、fromWebHandler(webHandler)——注意,其中的 fromPlainHandler / fromWebHandler 等已在 v2 中移除(见 8.2),因为 v2 本身就是 Web 标准的,不再需要来回桥接。

Web Streams 支持

v1.8 让 handler 能直接返回原生 ReadableStream 或 Response,天然兼容 Vercel/AI 这类依赖流式响应的库。

// v1.8 写法(历史):直接 return 一个 ReadableStream
export default eventHandler((event) => {
  setResponseHeader(event, "Content-Type", "text/html"); // v1 写法,v2 改 event.res.headers.set
  const encoder = new TextEncoder();
  const stream = new ReadableStream({
    async start(controller) {
      for (const token of "Streaming is so cool with H3!".split(" ")) {
        controller.enqueue(encoder.encode(token));
        await new Promise((resolve) => setTimeout(resolve, 300)); // 逐词推送
      }
    },
  });
  return stream;
});

这个能力延续到了 v2——只不过 v2 里你更常用 return iterable(...) 或 EventStream(见 5.2、7.6)来生成流,而不必手写 ReadableStream。

对象式 Event Handler

v1.8 支持用对象语法定义 handler,把运行在 handler 前后的一组钩子(比如鉴权、压缩中间件)和 handler 本身组织在一起。

// v1.8 写法(历史):对象语法把中间件和 handler 绑在一起
const auth = defineRequestMiddleware((event) => {
  event.context.auth = { name: "admin" };
});
const compression = defineResponseMiddleware((event) => { /* 压缩 */ });

export default eventHandler({
  onRequest: [auth],
  onResponse: [compression],
  async handler(event) {
    return `Hello ${event.context.auth?.name || "Guest"}`;
  },
});

注意 defineRequestMiddleware / defineResponseMiddleware 这两个工厂已在 v2 中移除——v2 改用 (event, next) 形式的中间件和 onRequest / onResponse 全局钩子(见 2.5、3.1、5.1)。

类型化请求

v1.8 开始支持用泛型标注事件输入类型,让 getQuery / readBody 等工具感知到 query 和 body 的形状。

// v1.8 写法(历史):泛型标注让请求工具具备类型
export default eventHandler<{ body: { name: string }; query: { id: string } }>(
  async (event) => {
    const query = getQuery(event);  // 类型推断为 { id: string }
    const body = await readBody(event); // 类型推断为 { name: string }
  },
);

v2 把这个思路推进成了「类型安全是内建的」(见 8.3 第五节),并配合 Standard Schema 做运行时校验。

运行时 + 类型安全的请求工具

v1.8 引入 getValidatedQuery(event, validator) 和 readValidatedBody(event, validator),把 Zod 这类 schema 校验器和 H3 打通,兼顾运行时与类型安全——这正是 6.2、7.7 里 readValidatedBody 的来源。

// v1.8 写法(历史):用 Zod 做校验,运行时 + 类型双保险
import { z } from "zod";

const userSchema = z.object({
  name: z.string().default("Guest"),
  email: z.string().email(),
});

export default eventHandler(async (event) => {
  const result = await readValidatedBody(event, (body) => userSchema.safeParse(body));
  if (!result.success) throw result.error.issues; // 校验失败抛问题列表
  return result.data; // 已校验且带类型
});

附加工具

v1.8 还补了一批实用函数:getRequestIP(event, { xForwardedFor? })(取客户端 IP,注意 v2 里对应的 getRequestProtocol 不再默认信任转发头,见 8.2)、readFormData(event)、clearResponseHeaders / removeResponseHeader、serveStatic(event, options)(跨平台静态资源服务)。

配合 HMR 的 TypeScript 开发

v1.8 同期更新了 listhen,配合 H3 应用实现带 TypeScript 支持和热模块替换(HMR)的开发服务器——你只需一个 index.ts,npx listhen@latest -w ./index.ts 即可启动。

// v1.8 写法(历史)
import { createApp, eventHandler } from "h3";

export const app = createApp();
app.use("/", () => "Hello world!");

读到这里,你应该能看清一条主线:v1.8 把"Web 标准兼容、流式、对象式 handler、类型化校验"这些种子埋进 H3,v2 再把它们长成完整重写。理解了这段来历,v2 那些"为什么这么改"的决定就不再是凭空而来。回到 8.1 把改动落到代码上,才是真正要做的迁移。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号