还是前端圈有意思,当你在NextJS,NuxtJS,Express,Nest上畅飞的时候,突然又看到了一个更加清凉的框架:hono.js
起源:
Hono 是由日本开发者 Yusuke Wada 在 2021 年 12 月创建的。根据其 GitHub 存储库的介绍,Hono 在日语中意为“火焰”,是“基于 Web 标准构建的一个小巧、简单、速度超快的 Web 框架”。它最初是为 Cloudflare Workers 构建的,但现在可以在“任何 JavaScript 运行时”上运行,包括 Node.js、Deno、Bun 和 Vercel(尽管 Node 支持需要适配器,并且 Node ≥ 18)。
可能也是一些灵感初现,作者开始了side project,觉得性能很好,所以把他做成了一个精巧的框架;
但是不得不踩一下国内的互联网环境,哪个公司又能让员工有自己的时间,去研发一个新的框架;
快速的路由匹配:
根据官网的设计哲学,为了达到快速响应,也采用了比较多路由匹配算法,如:
- RegExpRouter (速度最快)
- TrieRouter
- SmartRouter
- LinearRouter
- PatternRouter(最轻)
支持多种运行时:
- Cloudflare Workers (
workerd
)
- Deno
- Bun
- Fastly Compute
- AWS Lambda
- Node.js
- Vercel (edge-light)
按需引用功能
Hono 非常小巧,使用最小的预设
hono/tiny
,你可以在 12 KB 内写一个 "Hello World" 程序。这是因为它仅使用运行时内置的 Web 标准 API,且功能最小化。相较之下,Express 的打包大小为 579 KB。然而,你仍然可以实现许多功能。例如,实现基本身份验证略显麻烦,但 Hono 内置了基本身份验证中间件,你可以这样简单地把基本身份验证应用到
/auth/page
路径:import { Hono } from 'hono' import { basicAuth } from 'hono/basic-auth' const app = new Hono() app.use( '/auth/*', basicAuth({ username: 'hono', password: 'acoolproject', }) ) app.get('/auth/page', (c) => { return c.text('You are authorized') })
Hono 包包含的内置中间件还允许 Bearer 和 JWT 认证,以及 CORS 的简单配置。这些内置中间件不依赖外部库,但亦可使用许多第三方中间件,这些中间件允许使用外部库,例如使用 Clerk 和 Auth.js 进行身份验证的中间件,以及使用 Zod 和 Valibot 进行验证的中间件。
Hono 还提供了一些内置工具,如 Streaming 助手,对于实现 AI 功能非常有用。这些工具可以按需添加,并且只在添加时增加文件大小。在 Cloudflare Workers 中,Worker 的文件大小有一定限制。保持核心小巧,并通过中间件和助手扩展功能是非常合理的做法。
下面是一些 Hono 具备的中间件和辅助工具,能够大大提升开发效率:
- Basic Authentication
- Bearer Authentication
- Body Limit
- Cache
- Compress
- Context Storage
- Cookie
- CORS
- ETag
- html
- JSX
- JWT Authentication
- Logger
- Pretty JSON
- Secure Headers
- SSG
- Streaming
- GraphQL Server
- Firebase Authentication
- Sentry 等
通过引入中间件,Hono 可以实现更为复杂的功能。例如,添加基本身份验证中间件:
import { Hono } from 'hono' import { basicAuth } from 'hono/basic-auth' const app = new Hono() app.use( '/auth/*', basicAuth({ username: 'hono', password: 'acoolproject', }) ) app.get('/auth/page', (c) => { return c.text('You are authorized') })
自定义中间件
通过引入中间件,Hono 可以实现更为复杂的功能。例如,添加基本身份验证中间件:
import { Hono } from 'hono' import { basicAuth } from 'hono/basic-auth' const app = new Hono() app.use( '/auth/*', basicAuth({ username: 'hono', password: 'acoolproject', }) ) app.get('/auth/page', (c) => { return c.text('You are authorized') })
洋葱模型:
Hono 的重要概念是“处理器”和“中间件”。处理器是用户定义的用来接收请求并返回响应的函数。例如,你可以写一个处理器,获取查询参数的值,从数据库中检索数据,并以 JSON 格式返回结果。中间件可以处理来到处理器的请求和处理器返回的响应。你可以将中间件与其他中间件结合起来,构建更大、更复杂的应用程序,可以形象的称之为洋葱结构,“中间件”在 Handler 之前和之后执行,分别处理
Request
和 Response
app.use(async (c, next) => { const start = performance.now() await next() const end = performance.now() c.res.headers.set('X-Response-Time', `${end - start}`) })
默认使用TypeScript:
TypeScript可能已经算大多数开发者的唯一选择了;
RPC调用
Hono 拥有强大的类型系统。其中一个功能是 RPC(远程过程调用)。通过 RPC,你可以用 TypeScript 类型表达服务器端 API 规范。当这些类型在客户端作为泛型加载时,每个 API 端点的路径、参数和返回类型都会被推断出来,就像魔法一样。
例如,假设有一个用于创建博客文章的端点。这个端点接受一个
number
类型的 id
和一个 string
类型的 title
。使用 Zod(一个支持 TypeScript 推断的验证库),可以定义如下模式:import { z } from 'zod' const schema = z.object({ id: z.number(), title: z.string() })
然后创建一个处理程序,以 JSON 格式通过 POST 请求接收这个对象,并使用 Zod Validator 检查是否匹配模式。响应将具有一个名为
message
的字符串类型属性import { zValidator } from '@hono/zod-validator' const app=new Hono().basePath('/v1') // ... const routes= app.post('/posts', zValidator('json', schema), (c) => { const data= c.req.valid('json') return c.json({ message: `${data.id.toString()} is ${data.title}` }) })
这是一个“典型”的 Hono 处理程序,但是你可以通过
typeof
获取的 routes
类型将包含其 Web API 规范的信息。在此例中,它包括创建博客文章的端点——向 /posts
发送 POST 请求会返回一个 JSON 对象。
export type AppType = typeof routes
现在,我们来创建一个客户端。你将先前的
AppType
作为泛型传递给 Hono 客户端对象。import { hc } from 'hono/client' import { AppType } from '.' const client = hc<AppType>('http://localhost:8787')
设置完毕后,你就可以开始魔法操作了。代码补全工作完美无缺。当你写客户端代码时,不再需要完全了解 API 规范,这也有助于消除错误。
服务端 JSX
Hono 提供了内置的 JSX,这是一种允许你在 JavaScript 中编写类似 HTML 标签的代码的语法。提到 JSX,你可能首先想到 React,这是一个前端 UI 库。然而,Hono 的 JSX 最初是为了仅在服务器端运行而开发的。当首次开始开发 Hono 时,作者在寻找用于渲染 HTML 的模板引擎。大多数模板引擎,如 Handlebars 和 EJS,都在内部使用
eval
,而 eval
在 Cloudflare Workers 上不被支持。然后作者想到了使用 JSX。Hono 的 JSX 独特之处在于它将标签视为一个字符串。因此,以下代码实际上是可行的:
console.log((
<
h1
>
Hello
!<
/h1>).toString())
不需要像在 React 中那样调用
renderToString()
。如果你想渲染 HTML,只需返回这个字符串即可:app.get('/', (c) => c.html(
<
h1
>
Hello
<
/h1>))
非常有趣的是创建
Suspense
—— React 中的一个特性,它允许你在等待异步组件加载时显示一个后备 UI —— 无需任何客户端实现。异步组件在仅服务器实现中运行。服务器端 JSX 比你想象的要好玩。你可以用同样的方式为 Hono 的 JSX 复用 React 的 JSX 工具链,包括在编辑器中完成标签的功能,它们将成熟的前端技术带到了服务端。
编写测试
测试非常重要。幸运的是,使用 Hono 你可以轻松编写测试。
例如,让我们为一个接口编写测试。要测试对
/
的 GET 请求返回状态码 200
,你可以这样编写:it('should return 200 response', async () => {
const
res
=
await app.request('/')
expect(res.status).toBe(200)
})
很简单,这种测试的美妙之处在于你不需要启动服务器。Web 标准 API 将服务器层黑箱化。Hono 的内部测试代码有 20000 行,但大多数都像上面那样写成,不需要启动服务器。
走向全栈
Hono 于2024年2月发布了新的主要版本 4。有三个突出的主要功能:
- 静态站点生成
- 客户端组件
- 基于文件的路由
通过这些功能,我们可以在 Hono 中创建具有用户界面的全栈应用程序。客户端组件的引入支持 JSX 在客户端中工作,我们可以为页面添加交互,静态站点生成允许我们创建博客等,而不必将它们打包成一个 JavaScript 文件。
Hono
还启动了一个名为 HonoX 的实验项目。这是一个使用 Hono 和 Vite 的元框架,提供基于文件的路由和将客户端组件与服务端生成的 HTML 相结合的机制。更容易创建与 Cloudflare Pages 或 Workers 完美匹配的大型应用程序。此外,
Hono
还计划将其作为现有全栈框架(如 Remix 和 Qwik)的基础服务器运行。与起始于客户端的 React 项目 Next.js 相比,Hono 尝试从服务器端成为全栈框架。