标签 Hono 下的文章

从底层引擎优化角度,深入剖析 Hono 路由器的极致性能奥秘

引言

在众多 JavaScript Web 框架中,Hono 以其极致的性能表现脱颖而出。特别是在 Cloudflare Workers、Deno 等边缘计算环境中,Hono 的路由匹配速度在同类框架中名列前茅。这背后的秘密是什么?答案就藏在它的 RegExpRouter 实现中。

⚠️ 重要提示:本文重点讨论 RegExpRouter 的核心原理。在实际应用中,Hono 使用 SmartRouter 自动组合多种路由器(RegExpRouter、TrieRouter、LinearRouter)以达到最佳性能。

本文将通过一个精简的实现,深入剖析 Hono 路由器的核心原理,帮助你理解:

  • 为什么 RegExp 路由比传统路由快
  • 路由是如何预编译的
  • 参数提取的巧妙设计

📌 关键要点

项目说明
核心原理将路由预编译为正则表达式,利用引擎底层优化
性能优势减少 JS 层开销,一次原生调用完成匹配
最佳环境Cloudflare Workers、Deno 等边缘计算平台
实际架构SmartRouter + RegExpRouter + TrieRouter 组合
权衡考虑Node.js 环境性能打折扣,路由注册较慢

传统路由的性能瓶颈

在理解 Hono 的优化之前,我们先看看传统路由的处理方式:

// 传统路由的典型实现
function matchRoute(path, routes) {
  for (const route of routes) {
    // 1. 字符串分割
    const pathParts = path.split('/');
    const routeParts = route.path.split('/');

    // 2. 逐段比较
    if (pathParts.length !== routeParts.length) continue;

    const params = {};
    let matched = true;

    // 3. JS 层面的循环和条件判断
    for (let i = 0; i < pathParts.length; i++) {
      if (routeParts[i].startsWith(':')) {
        params[routeParts[i].slice(1)] = pathParts[i];
      } else if (pathParts[i] !== routeParts[i]) {
        matched = false;
        break;
      }
    }

    if (matched) return { route, params };
  }
  return null;
}

性能问题在哪里?

  • ❌ 大量的字符串操作(splitslice
  • ❌ 多层循环和条件判断,全在 JavaScript 层面执行
  • ❌ 每个请求都要重复这些操作

Hono 的解决方案:下沉到引擎层

Hono 的核心思想非常简单却极其巧妙:

将路由匹配逻辑从 JavaScript 层下沉到 JavaScript 引擎底层(C++ 实现的正则表达式引擎)

1. 预编译阶段(应用启动时)

在应用启动时,Hono 会将路由路径转换为正则表达式:

// 路由路径:/user/:id/posts/:postId
// 转换为正则:/^\/user\/([^/]+)\/posts\/([^/]+)$/

const paramNames: string[] = [];

const regexPath = path.replace(/:([a-zA-Z0-9_]+)/g, (_, paramName) => {
  paramNames.push(paramName); // 记录参数名
  return '([^/]+)';           // 替换为捕获组
});

const pattern = new RegExp(`^${regexPath}$`);

关键点:

  • :id([^/]+):匹配除 / 外的任意字符
  • 参数名被保存在 paramNames 数组中
  • 这个"昂贵"的编译过程只在启动时执行一次

2. 匹配阶段(请求到来时)

当请求到来时,只需要一行代码:

const match = pattern.exec(path);

这行代码的魔法:

  • exec 是 JavaScript 引擎的原生方法,由 C++ 实现
  • ✅ 正则引擎在底层进行了高度优化(状态机、JIT 编译等)
  • ✅ 无需手动分割字符串、无需 JS 层循环
  • ✅ 参数提取通过捕获组自动完成

3. 参数提取

匹配成功后,参数提取也非常简洁:

const params: Record<string, string> = {};
route.paramNames.forEach((name, index) => {
  params[name] = match[index + 1]; // match[0] 是完整匹配
});

完整实现解析

让我们看完整的简化实现,包含所有必要的类型定义:

// ============ 类型定义 ============

/**
 * 路由处理函数类型
 * @param params - 从 URL 中提取的参数对象
 */
type Handler = (params: Record<string, string>) => void;

/**
 * 路由信息接口
 */
interface Route {
  method: string;        // HTTP 方法(GET, POST, etc.)
  path: string;          // 原始路径模式(如 /user/:id)
  handler: Handler;      // 路由处理函数
  paramNames: string[];  // 参数名数组(如 ['id', 'postId'])
}

/**
 * 匹配结果接口
 */
interface MatchResult {
  handler: Handler;
  params: Record<string, string>;
}

// ============ 核心路由器实现 ============

export class DemoRegExpRouter {
  // 存储预编译的正则表达式和路由信息
  private routes: { pattern: RegExp; route: Route }[] = [];

  // 注册路由:预编译
  add(method: string, path: string, handler: Handler) {
    const paramNames: string[] = [];

    // 将 :param 转为正则捕获组
    const regexPath = path.replace(/:([a-zA-Z0-9_]+)/g, (_, paramName) => {
      paramNames.push(paramName);
      return '([^/]+)';
    });

    const pattern = new RegExp(`^${regexPath}$`);
    this.routes.push({ pattern, route: { method, path, handler, paramNames } });
  }

  // 匹配路由:执行正则
  match(method: string, path: string) {
    for (const { pattern, route } of this.routes) {
      if (route.method !== method && route.method !== 'ALL') continue;

      const match = pattern.exec(path); // 核心:原生正则匹配

      if (match) {
        const params: Record<string, string> = {};
        route.paramNames.forEach((name, index) => {
          params[name] = match[index + 1];
        });
        return { handler: route.handler, params };
      }
    }
    return null;
  }
}

实战示例

const router = new DemoRegExpRouter();

// 注册路由
router.add('GET', '/user/:id', (params) => {
  console.log(`获取用户详情,ID: ${params.id}`);
});

router.add('GET', '/user/:id/posts/:postId', (params) => {
  console.log(`获取用户 ${params.id} 的文章 ${params.postId}`);
});

// 匹配请求
const result = router.match('GET', '/user/123/posts/456');
if (result) {
  result.handler(result.params);
  // 输出: 获取用户 123 的文章 456
}

性能优势分析

为什么 RegExpRouter 更快?

  1. 减少 JS 执行开销:从多层循环降到一次原生调用
  2. 引擎优化:正则引擎经过数十年优化,包含 JIT、状态机等技术
  3. 减少内存分配:无需频繁创建数组、对象
  4. 一次性编译:预编译阶段完成所有准备工作,请求时零额外开销

性能对比概览

方案实现层次执行效率适用场景
传统路由JavaScript 层中等通用场景
RegExpRouter引擎底层极快边缘计算、高并发
📊 实际 Benchmark:根据 Hono 官方测试,在 Cloudflare Workers 和 Deno 环境中,Hono 是同类框架中最快的。但在 Node.js 环境中,由于适配器开销,性能优势会打折扣。

Hono 真实实现的进一步优化

本文的简化版每个路由都是独立的正则表达式。而 Hono 的真实实现采用了多路由器协同的策略:

1. SmartRouter(智能路由器)

Hono 默认使用 SmartRouter,它会:

  • 根据路由特征自动选择最快的路由器
  • 动态组合 RegExpRouter、TrieRouter、LinearRouter
  • 检测路由模式并持续使用最优方案

2. 路由器分工

路由器适用场景特点
RegExpRouter动态参数路由最快,但不支持所有模式
TrieRouter复杂路由模式支持所有模式,性能优秀
LinearRouter少量路由简单可靠,路由少时够用

3. 实际优化策略

  • 静态路由优先/about/contact 等使用 Map 快速查找
  • 路由预分组:按 HTTP 方法分组,减少匹配次数
  • 延迟编译:RegExpRouter 编译较慢,适合应用启动时初始化,不适合无服务器环境的冷启动

这些优化使得 Hono 在处理数百个路由时仍然保持极高性能。

局限性与权衡

RegExpRouter 并非银弹,在使用时需要注意以下限制:

RegExpRouter 的局限

  • 不支持所有路由模式:某些复杂的路由规则无法用简单正则表示
  • 注册阶段较慢:路由编译需要时间,不适合每次请求都重新初始化的环境(如某些无服务器冷启动场景)
  • 调试难度:编译后的正则表达式可读性较差,出错时难以定位
  • 模式约束:如带严格约束的参数 /:id(\\d+) 需要额外处理

运行环境差异

环境性能表现原因
Cloudflare Workers⭐⭐⭐⭐⭐ 极快原生 Web 标准 API
Deno⭐⭐⭐⭐⭐ 极快原生 Web 标准 API
Bun⭐⭐⭐⭐ 很快高性能 JS 运行时
Node.js⭐⭐⭐ 中等需要适配器转换
💡 选型建议:如果你的应用运行在边缘计算环境(Cloudflare Workers、Vercel Edge Functions 等),Hono 是绝佳选择。如果是传统 Node.js 服务器,Fastify 可能是更好的选择。

但对于大多数 Web 应用场景,RegExpRouter 的性能与功能平衡已经足够优秀。

总结

Hono 的 RegExpRouter 通过"预编译 + 引擎下沉"的策略,在特定环境中将路由匹配性能提升到极致:

核心优势

  1. 预编译:启动时一次性将路由转为正则,摊销成本
  2. 引擎下沉:利用 C++ 实现的正则引擎,避免 JS 层开销
  3. 参数捕获:巧妙利用正则捕获组,无需手动解析
  4. 智能组合:SmartRouter 自动选择最优路由器,兼顾性能与功能

设计哲学

这种设计哲学值得我们思考:性能优化的终极目标,往往是让"热路径"代码尽可能多地运行在更底层的优化环境中

对于 Web 框架来说,路由匹配就是最热的路径之一。Hono 在边缘计算环境中找到了这个问题的最优解,同时通过多路由器架构保证了广泛的适用性。

最佳实践

  • 边缘计算/Serverless:Hono 是首选(Cloudflare Workers、Deno Deploy)
  • 高并发场景:充分发挥 RegExpRouter 性能优势
  • ⚠️ Node.js 环境:考虑性能权衡,或选择 Fastify 等专门优化的框架
  • ⚠️ 复杂路由规则:了解 RegExpRouter 的限制,必要时使用 TrieRouter

延伸阅读:

技术资源:


📝 文章说明

本文基于对 Hono 框架的学习和研究编写,代码示例为简化版实现,用于教学目的。实际的 Hono RegExpRouter 实现更加复杂和优化。

性能数据和对比来自 Hono 官方文档和社区测试,具体数值会因测试环境、负载类型、运行时版本等因素而异。实际使用时请根据自己的场景进行测试。

如果您发现文中有任何不准确之处,欢迎指正。

一个基于 Hono 的全栈 Web 框架,结合了 Islands 架构和边缘计算的强大能力

引言

在现代 Web 开发中,我们面临着一个永恒的挑战:如何在提供丰富交互体验的同时,保持快速的加载速度和优秀的性能?传统的单页应用(SPA)虽然交互流畅,但首屏加载慢、SEO 困难;而传统的服务端渲染(SSR)虽然首屏快,但缺乏现代前端框架的开发体验。

HonoX 的出现,为这个问题提供了一个优雅的解决方案。它是基于超快的 Hono Web 框架构建的全栈框架,采用 Islands 架构,完美平衡了性能和开发体验。

什么是 HonoX?

HonoX 是一个全栈 Web 框架,它建立在 Hono 之上。Hono 是一个轻量级、超快速的 Web 框架,可以运行在任何 JavaScript 运行时(Cloudflare Workers、Deno、Bun、Node.js 等)。

核心特性

  1. Islands 架构 - 渐进式水合,只在需要的地方加载 JavaScript
  2. 文件路由系统 - 基于文件系统的直观路由
  3. 边缘优先 - 为 Cloudflare Workers 等边缘运行时优化
  4. 类型安全 - 完整的 TypeScript 支持
  5. 零配置 - 开箱即用的最佳实践
  6. 极致性能 - 继承 Hono 的超快性能

Islands 架构:重新思考前端水合

什么是 Islands 架构?

Islands 架构是一种现代前端架构模式,最早由 Etsy 的前端架构师 Katie Sylor-Miller 提出,后来被 Astro、Fresh 等框架采用。

想象一个网页是一片海洋,而需要交互的组件是海洋中的"岛屿":

┌─────────────────────────────────┐
│  静态 HTML(服务端渲染)          │
│                                 │
│  ┌─────────┐      ┌─────────┐  │
│  │ Island  │      │ Island  │  │
│  │ (交互)  │      │ (交互)  │  │
│  └─────────┘      └─────────┘  │
│                                 │
│         ┌─────────┐             │
│         │ Island  │             │
│         │ (交互)  │             │
│         └─────────┘             │
└─────────────────────────────────┘

这种架构的优势在于:

  • 减少 JavaScript 负载 - 只加载真正需要的 JavaScript
  • 提升首屏性能 - 静态内容立即可见
  • 渐进式增强 - 交互组件逐步加载和激活
  • 更好的 SEO - 完整的服务端渲染内容

HonoX 中的 Islands

在 HonoX 中使用 Islands 非常简单:

// app/islands/Counter.tsx
import { useState } from 'hono/jsx'

export default function Counter({ initialCount = 0 }) {
  const [count, setCount] = useState(initialCount)

  return (
    <div class="counter">
      <button onClick={() => setCount(count - 1)}>-</button>
      <span>{count}</span>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  )
}

只需将组件放在 app/islands/ 目录下,HonoX 会自动处理:

  • 服务端渲染
  • 客户端代码分割
  • 按需水合

在页面中使用:

// app/routes/index.tsx
import Counter from '../islands/Counter'

export default function Home() {
  return (
    <div>
      <h1>我的页面</h1>
      <p>这段文字是纯静态的,不需要 JavaScript</p>
      <Counter initialCount={0} />
    </div>
  )
}

文件路由系统:约定优于配置

HonoX 采用基于文件的路由系统,让路由管理变得直观:

app/routes/
├── index.tsx           → /
├── about.tsx           → /about
├── blog/
│   ├── index.tsx       → /blog
│   └── [slug].tsx      → /blog/:slug
└── api/
    ├── users.ts        → /api/users
    └── users/
        └── [id].ts     → /api/users/:id

动态路由

使用方括号定义动态路由参数:

// app/routes/blog/[slug].tsx
import { createRoute } from 'honox/factory'

export default createRoute((c) => {
  const { slug } = c.req.param()

  return c.render(
    <article>
      <h1>文章:{slug}</h1>
    </article>
  )
})

API 路由

API 路由返回 JSON 数据:

// app/routes/api/users/[id].ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const app = new Hono()

// GET /api/users/:id
app.get('/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id, name: 'User ' + id })
})

// POST /api/users/:id
const schema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
})

app.post('/:id', zValidator('json', schema), (c) => {
  const data = c.req.valid('json')
  const id = c.req.param('id')

  return c.json({
    id,
    ...data,
    updated: true
  })
})

export default app

中间件系统:强大且灵活

HonoX 继承了 Hono 的中间件系统,让你可以轻松处理横切关注点:

// app/routes/_middleware.tsx
import { createRoute } from 'honox/factory'
import { compress } from 'hono/compress'
import { logger } from 'hono/logger'
import { secureHeaders } from 'hono/secure-headers'

export default createRoute((c, next) => {
  // 日志记录
  logger()(c, next)

  // 安全头
  secureHeaders()(c, next)

  // 响应压缩
  compress()(c, next)

  return next()
})

自定义中间件

创建自定义中间件也很简单:

// 性能计时中间件
export const timing = createMiddleware(async (c, next) => {
  const start = Date.now()
  await next()
  const end = Date.now()

  c.header('Server-Timing', `total;dur=${end - start}`)
})

// 认证中间件
export const auth = createMiddleware(async (c, next) => {
  const token = c.req.header('Authorization')

  if (!token) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  // 验证 token...
  await next()
})

性能优化:从框架层面开始

HonoX 内置了多种性能优化:

1. 自动代码分割

每个 Island 组件自动分割成独立的 chunk:

// 自动生成类似这样的输出
dist/
├── client/
│   ├── island-Counter.js    (3KB)
│   ├── island-Search.js     (5KB)
│   └── island-Modal.js      (4KB)
└── server/
    └── index.js

2. 流式 SSR

使用 Suspense 实现流式渲染:

import { Suspense } from 'hono/jsx'
import AsyncData from '../islands/AsyncData'

export default function Page() {
  return (
    <div>
      <h1>立即显示的标题</h1>

      <Suspense fallback={<div>加载中...</div>}>
        <AsyncData />
      </Suspense>
    </div>
  )
}

页面渲染流程:

  1. 立即发送 HTML 头部和静态内容
  2. 异步组件准备好后流式发送
  3. 最后发送激活脚本

3. 智能缓存策略

// 静态资源长期缓存
app.get('/static/*', async (c) => {
  c.header('Cache-Control', 'public, max-age=31536000, immutable')
  return c.next()
})

// API 响应 ETag 缓存
app.get('/api/data', async (c) => {
  const data = await fetchData()
  const etag = generateETag(data)

  if (c.req.header('If-None-Match') === etag) {
    return c.body(null, 304)
  }

  c.header('ETag', etag)
  return c.json(data)
})

类型安全:端到端的 TypeScript

HonoX 提供完整的类型安全,从路由到 API:

// 定义 API 类型
type User = {
  id: string
  name: string
  email: string
}

// API 路由自动推断类型
const app = new Hono<{ Variables: { user: User } }>()

app.get('/api/user', (c) => {
  const user = c.get('user') // 类型:User
  return c.json(user)
})

// 在客户端使用类型
const response = await fetch('/api/user')
const user: User = await response.json()

部署:边缘优先

HonoX 针对边缘运行时优化,特别是 Cloudflare Workers:

Cloudflare Pages 部署

# 构建
npm run build

# 部署
npm run deploy

优势:

  • 全球 CDN - 300+ 个边缘节点
  • 零冷启动 - Workers 即时响应
  • 自动扩展 - 无需配置
  • 低成本 - 免费层每天 100,000 请求

其他平台

HonoX 也支持部署到:

  • Vercel - 使用 Node.js 适配器
  • Netlify - Edge Functions
  • Deno Deploy - 原生支持
  • 传统服务器 - Node.js

实战案例:构建一个博客

让我们用 HonoX 构建一个完整的博客系统:

1. 文章列表页

// app/routes/blog/index.tsx
import { createRoute } from 'honox/factory'
import { getPosts } from '../../lib/posts'

export default createRoute(async (c) => {
  const posts = await getPosts()

  return c.render(
    <div class="blog">
      <h1>博客文章</h1>
      <ul>
        {posts.map(post => (
          <li key={post.slug}>
            <a href={`/blog/${post.slug}`}>
              <h2>{post.title}</h2>
              <time>{post.date}</time>
            </a>
          </li>
        ))}
      </ul>
    </div>
  )
})

2. 文章详情页

// app/routes/blog/[slug].tsx
import { createRoute } from 'honox/factory'
import { getPost } from '../../lib/posts'
import CommentSection from '../../islands/CommentSection'

export default createRoute(async (c) => {
  const { slug } = c.req.param()
  const post = await getPost(slug)

  if (!post) {
    return c.notFound()
  }

  return c.render(
    <article>
      <header>
        <h1>{post.title}</h1>
        <time>{post.date}</time>
        <div>{post.author}</div>
      </header>

      <div dangerouslySetInnerHTML={{ __html: post.content }} />

      {/* 评论区使用 Island 实现交互 */}
      <CommentSection postId={slug} />
    </article>,
    {
      title: post.title,
      description: post.excerpt,
    }
  )
})

3. 交互式评论组件

// app/islands/CommentSection.tsx
import { useState } from 'hono/jsx'

type Comment = {
  id: string
  author: string
  content: string
  createdAt: string
}

export default function CommentSection({ postId }: { postId: string }) {
  const [comments, setComments] = useState<Comment[]>([])
  const [loading, setLoading] = useState(false)

  const loadComments = async () => {
    setLoading(true)
    const res = await fetch(`/api/comments/${postId}`)
    const data = await res.json()
    setComments(data.comments)
    setLoading(false)
  }

  const submitComment = async (e: Event) => {
    e.preventDefault()
    const form = e.target as HTMLFormElement
    const formData = new FormData(form)

    await fetch(`/api/comments/${postId}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        author: formData.get('author'),
        content: formData.get('content'),
      }),
    })

    form.reset()
    loadComments()
  }

  return (
    <section class="comments">
      <h2>评论</h2>

      <button onClick={loadComments}>
        {loading ? '加载中...' : '加载评论'}
      </button>

      {comments.map(comment => (
        <div key={comment.id} class="comment">
          <strong>{comment.author}</strong>
          <p>{comment.content}</p>
          <time>{comment.createdAt}</time>
        </div>
      ))}

      <form onSubmit={submitComment}>
        <input name="author" placeholder="您的名字" required />
        <textarea name="content" placeholder="评论内容" required />
        <button type="submit">提交评论</button>
      </form>
    </section>
  )
}

4. 评论 API

// app/routes/api/comments/[postId].ts
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'

const app = new Hono()

const commentSchema = z.object({
  author: z.string().min(1).max(50),
  content: z.string().min(1).max(1000),
})

// 获取评论
app.get('/:postId', async (c) => {
  const { postId } = c.req.param()

  // 从数据库获取评论
  const comments = await db.comments
    .where('postId', postId)
    .orderBy('createdAt', 'desc')
    .get()

  return c.json({ comments })
})

// 添加评论
app.post('/:postId', zValidator('json', commentSchema), async (c) => {
  const { postId } = c.req.param()
  const data = c.req.valid('json')

  const comment = await db.comments.create({
    postId,
    ...data,
    createdAt: new Date().toISOString(),
  })

  return c.json(comment, 201)
})

export default app

与其他框架对比

HonoX vs Next.js

HonoX 的优势:

  • 更轻量(核心更小)
  • 边缘优先设计
  • 更简单的学习曲线
  • 更快的冷启动

Next.js 的优势:

  • 更成熟的生态系统
  • 更多的官方集成
  • React Server Components
  • 更强大的图像优化

HonoX vs Astro

相似点:

  • 都使用 Islands 架构
  • 都注重性能
  • 都支持多框架

HonoX 的优势:

  • 更好的 API 路由
  • 原生支持边缘运行时
  • 更轻量的运行时

Astro 的优势:

  • 可以混用多个前端框架
  • 更丰富的内容处理功能
  • 更好的静态站点生成

HonoX vs Fresh

相似点:

  • 都基于 Islands 架构
  • 都使用文件路由
  • 都注重性能

HonoX 的优势:

  • 支持更多运行时
  • 更灵活的中间件系统
  • 基于 Hono 的强大生态

Fresh 的优势:

  • Deno 原生集成
  • Preact 默认支持
  • 更简单的配置

最佳实践

1. 合理使用 Islands

✅ 好的做法:

// 只将需要交互的部分做成 Island
<article>
  <h1>{title}</h1>
  <p>{content}</p>
  <ShareButtons /> {/* Island */}
  <CommentSection /> {/* Island */}
</article>

❌ 避免:

// 不要把整个页面都做成 Island
export default function Page() {
  const [state, setState] = useState()
  // 整个页面都会在客户端水合
}

2. 优化数据获取

✅ 好的做法:

// 在服务端并行获取数据
export default createRoute(async (c) => {
  const [user, posts, comments] = await Promise.all([
    getUser(),
    getPosts(),
    getComments(),
  ])

  return c.render(<Page user={user} posts={posts} comments={comments} />)
})

❌ 避免:

// 避免串行请求
const user = await getUser()
const posts = await getPosts() // 等待上一个完成
const comments = await getComments() // 又要等待

3. 使用流式渲染

// 对于慢速数据使用 Suspense
export default function Page() {
  return (
    <>
      <Header /> {/* 快速渲染 */}

      <Suspense fallback={<Skeleton />}>
        <SlowData /> {/* 异步加载 */}
      </Suspense>

      <Footer />
    </>
  )
}

4. 实现有效缓存

// 分层缓存策略
const app = new Hono()

// 1. 边缘缓存
app.use('/api/*', cache({
  cacheName: 'api-cache',
  cacheControl: 'max-age=60',
}))

// 2. 浏览器缓存
app.use('/static/*', async (c, next) => {
  await next()
  c.header('Cache-Control', 'public, max-age=31536000, immutable')
})

// 3. 条件请求
app.use('/data/*', etag())

未来展望

HonoX 还在快速发展中,以下是一些令人期待的方向:

  1. 更多的运行时支持 - 包括 AWS Lambda、Azure Functions 等
  2. 增强的开发工具 - 更好的调试体验、性能分析工具
  3. 更丰富的生态 - 官方插件、第三方集成
  4. 框架无关的 Islands - 支持 React、Vue、Svelte 等
  5. 增量静态生成 - 类似 Next.js 的 ISR

结论

HonoX 代表了现代全栈框架的一个重要方向:

  • 性能优先 - Islands 架构和边缘计算
  • 开发体验 - 简单直观的 API
  • 灵活性 - 支持多种运行时和部署方式
  • 类型安全 - 完整的 TypeScript 支持

如果你正在寻找一个轻量、快速、现代的全栈框架,特别是需要部署到边缘运行时,HonoX 是一个值得考虑的选择。

虽然它还比较年轻,生态系统不如 Next.js 那样成熟,但它的设计理念和技术方向都非常正确。随着 Hono 生态的发展,HonoX 也将变得越来越强大。

资源链接


欢迎在评论区分享你对 HonoX 的看法和使用经验!

复盘一下我 vibe coding 一周,开发 WorkAny 的过程,很有意思。😂

开发过程

  1. 上周三在香港办卡,临时起意想做个桌面 Agent 项目,对标 cowork ,晚上回到广州开始写代码

  2. 初期目标是快速发布,没时间去研究哪个 Agent 框架好用了,看很多人在用 claude agent sdk ,先用这个吧

  1. 第一时间想到用 tauri ,喜欢小而美,总觉得 electron 很重,不想用

  2. 不想自己写代码了,决定让 claude code 来写。之前的 claude 账号都被封了,用不上原版 cc ,装了个 cc-switch ,接上 OpenRouter 的 API 开始写

  3. 截了个 chatbot 的交互截图,让 cc 参考着先把基本的对话流程跑通,用 claude agent sdk ,接上 OpenRouter ,cc 很快写完了第一版

  1. tauri 本质是用 rust 的壳子套了个前端界面,不熟悉 rust ,让 cc 用 hono 写 API ,rust 只做壳子,不做业务功能。API 作为 sidecar 打包进 app

  1. 让 cc 在 API 引入 sqlite 实现本地存储,持久化任务数据,创建本地工作目录,保存任务输出文件

  2. 写了半天,看 OpenRouter 消耗了 110 刀,有点肉疼。买了个美国住宅 ip ,付费上了原版 claude pro

  3. 截了个 Manus 的任务详情图,让 cc 参考写完工具调用的逻辑,中间是 chatbot 对话,右边用一个虚拟计算机的容器展示输入输出

  1. 让 cc 接入 shadcn/ui ,把样式做得好看一点,支持切换皮肤

  1. 又写了一天,关键时候 claude pro 限频了,很影响心情,补差价上了 claude max 顶配版

  2. 让 cc 把自定义模型配置,mcp 、skills 调用的逻辑都实现了,跑了几个生成 PPT 、Excel 、Doc 、 网页的 case ,效果不错

  1. 让 cc 把输出文件夹和中间过程的 artifacts 都在右边展示出来,写了个 artifact preview 容器,渲染各种类型的文件,可视化预览

  1. 有些任务需要跑脚本完成,考虑到用户电脑可能没装代码运行环境,让 cc 引入 sandbox 来运行代码

  1. 考虑到扩展性,需要支持不同类型的 Agent runtime 和 sandbox ,让 cc 写了两个抽象类,统一接口调用。Agent runtime 支持 claude code 、codex 、deepagents ,sandbox 支持 boxlite 、codex-sandbox 、claude-sandbox

  1. 觉得 cc 写的代码有点乱,让 cc 引入 eslint 和 prettier 做了下格式化,把逻辑太多的文件做模块化拆分。再参考 ShipAny 的目录结构,调整了一下项目结构

  1. 让 cc 写打包脚本,构建不同操作系统的安装包。把安装包发给一些朋友,开始内测了。根据内测用户的反馈,再让 cc 继续优化逻辑,解决问题,迭代功能

  1. 有些用户电脑没装 node ,没有 claude code ,安装软件后跑不起来,让 cc 在构建脚本支持 flag 参数,把 node 和 cc 作为 sidecar 打包进 app ,让用户能够开箱即用

  2. Mac 用户安装 app 后提示文件损坏或有安全提示,让 cc 在构建脚本里面加上签名处理,用我的 Apple 开发者账户对打包的 Mac app 做签名

  3. node 和 cc 都打包进 app 的版本,安装包 100 多 m ,有点重。让 cc 在构建脚本实现默认不打包,在用户启动 app 的时候引导安装 node 和 cc ,精简版安装包才 20 多 m ,小巧精致

  1. app 基本功能实现得差不多了,让 cc 在 ShipAny 模板基础上写一个 WorkAny 的官网,放上演示图,部署上线

  1. WorkAny 开源发布,MVP 版本上线,用户拉源码本地构建,配个 API 直接用

  1. 让 cc 写了个 github 构建脚本,在代码推送到 main 分支时,自动触发 github action 构建,一次性打包 Windows 、Linux 、Mac 三大平台的安装包,自动发布到 release ,用户无需自行构建了

  1. 根据用户的反馈,问题丢给 cc 去修,想到什么新功能也告诉 cc 加上,自己只做测试,不写代码,看都不看一眼。🌚


几点感悟

  1. 第一次尝试全自动驾驶 vibe coding 做项目,爽感非常强烈,WorkAny 的代码 100% 由 cc 老弟完成,我只负责指挥,日常开三个窗口,让三个 cc 老弟同时干活,效率拉满

  1. AI 时代技术平权,人人都是建筑师,理解用户需求、好的产品 sense 和审美是做出好产品的关键

  1. 技术广度和全局视野是最大的优势,可以精准提需求,指哪打哪,遇到问题能快速定位,防止 AI 走偏失控

  2. 以前总觉得手洗的衣服比洗衣机洗的干净,现在可以放心交给洗衣机了,又干净又快,能穿就行

  3. 优秀的程序员不会被 AI 淘汰,法拉利老了还是法拉利。🌝

欢迎试用 WorkAny ,感谢反馈与支持。

https://workany.ai