mcpField
H
hono-inertia

hono-inertia

github.com/yusukebe开发3699 次安装23 周前更新
代码仓库

关于 hono-inertia

面向 TypeScript 开发者的 Hono、Inertia.js 与 hono/jsx 单栈 SPA 构建指南,无需单独编写前端或 JSON API 层。它还介绍页面属性类型生成,以及 Vite 和 Cloudflare Workers 部署要点。

主要功能

  1. 01使用 Hono 返回 Inertia 页面
  2. 02以 hono/jsx 构建客户端页面
  3. 03生成页面属性类型
  4. 04通过 Vite 打包和 SSR
  5. 05配置 Cloudflare Workers 部署

适用场景

  1. 01为 Hono 应用添加客户端状态
  2. 02构建乐观 UI 和复杂表单
  3. 03避免为 SPA 单独维护前端栈

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yusukebe/skills --skill hono-inertia

安装后在 Claude Code 中输入/hono-inertia确认已加载。

详细介绍

使用 Inertia.js + Hono + hono/jsx 构建 SPA 体验的应用

Inertia.js 让你无需编写单独的前端或 JSON API 层,即可构建单页应用。服务端返回 Inertia 响应(页面名称 + 属性),轻量客户端适配器会即时切换当前页面组件。使用 Hono 作为服务端、hono/jsx 作为客户端,你可以在同一套 TypeScript 技术栈中获得 SPA 的交互体验,而且无需使用 React。

当服务端渲染的 hono/jsx 已无法满足需求时,可以考虑这种方案——也就是说,当你需要客户端状态、乐观 UI 或丰富的表单时。

技术栈

  • 服务端: hono + @hono/inertia。inertia({ rootView }) 中间件会添加 c.render(name, props),用于返回 Inertia 响应。
  • 客户端: @ts-76/inertia-hono-jsx。它基于 @inertiajs/core 和 hono/jsx/dom 提供 createInertiaApp、<Link>、<Form>、<Head>、useForm、useHttp 等功能。
  • 打包 / SSR: Vite(部署到 Workers 时使用 @cloudflare/vite-plugin)和 vite-ssr-components,用于配置资源。可从 cloudflare-workers+vite Hono starter template 开始——它已配置好 Vite + Workers:https://github.com/honojs/starter/tree/main/templates/cloudflare-workers+vite。
  • 页面类型: @hono/inertia/vite 会生成 pages.gen.ts,使 PagePropsFor<Name> 能解析为传给 c.render(Name, ...) 的属性类型。

参考示例

yusukebe/hono-inertia-example 是标准的项目布局(在 Cloudflare Workers 上使用 Hono + Inertia)。它使用 React 编写,因此不要原样复制其中的 app/client.tsx 和 app/root-view.tsx——需要改用 @ts-76/inertia-hono-jsx 和 hono/jsx 重写。服务端部分(app/server.ts、路由结构、Vite 配置、wrangler.jsonc、使用 @hono/zod-validator 进行验证)则可直接沿用。

https://github.com/yusukebe/hono-inertia-example

服务端示例

// app/server.ts
import { Hono } from 'hono'
import { inertia } from '@hono/inertia'
import { rootView } from './root-view'

const app = new Hono()

app.use(inertia({ rootView }))

const routes = app
  .get('/', (c) => c.render('Home', { message: 'Hono x Inertia' }))
  .get('/users', (c) => c.render('Users/Index', { users: listUsers() }))
  .get('/users/:id{[0-9]+}', (c) => {
    const id = Number(c.req.param('id'))
    const user = findUser(id)
    if (!user) return c.notFound()
    return c.render('Users/Show', { user })
  })

export default routes

c.render(name, props) 返回完整的 HTML 文档(首次请求时)或 JSON Inertia 页面对象(后续客户端导航时)——@hono/inertia 会检查 X-Inertia 请求标头来决定返回哪种内容。

客户端示例(hono/jsx)

// app/client.tsx
import { createInertiaApp } from '@ts-76/inertia-hono-jsx'

createInertiaApp({
  resolve: (name) => {
    const pages = import.meta.glob('./pages/**/*.tsx', { eager: true })
    return pages[`./pages/${name}.tsx`]
  },
})

如果不设置自定义 setup,适配器会为你挂载 <App />。当根元素带有 data-server-rendered 时,它会使用 hydrateRoot(来自 hono/jsx/dom),否则使用 createRoot。

页面组件(hono/jsx)

// app/pages/Users/Index.tsx
import { Head, Link, type PageComponent } from '@ts-76/inertia-hono-jsx'

const UsersIndex: PageComponent<'Users/Index'> = ({ users }) => (
  <main>
    <Head title='Users' />
    <h1>Users</h1>
    {users.map((u) => (
      <Link href={`/users/${u.id}`} key={u.id}>
        {u.name}
      </Link>
    ))}
  </main>
)

export default UsersIndex

PageComponent<'Users/Index'> 会从 pages.gen.ts 获取属性类型。该文件由 @hono/inertia/vite 根据服务端路由生成,因此服务端与客户端的属性不匹配会导致构建错误,而非运行时错误。

默认设置

  • 在 tsconfig.json 中设置 jsxImportSource: "hono/jsx"。
  • 在 root-view.tsx 中使用 vite-ssr-components/hono(而非 /react)来注入 Vite 客户端和资源标签。
  • 使用 @cloudflare/vite-plugin 部署,这样 wrangler deploy 就会一并发布 SSR Worker 和客户端包。

注意事项

  • @ts-76/inertia-hono-jsx 属于社区维护,且根据其 README,对 hono/jsx 的支持是“部分的”——浏览器渲染针对 hono/jsx/dom,但服务端渲染的还原度达不到 React 的水平。对于任何较复杂的页面,都应验证 SSR 输出。
  • Inertia 不是 API 框架。如果非 Inertia 客户端(移动端、第三方客户端)需要相同的数据,请构建单独的 JSON 端点——不要尝试复用 Inertia 响应结构。
  • 资源版本控制:确保 X-Inertia-Version 与客户端包哈希匹配,否则客户端会在每次导航时静默强制完整重新加载。
  • 在 Cloudflare Workers 上,通过 Workers Assets 提供客户端包(@cloudflare/vite-plugin 会处理此事),不要通过 fetch 从 R2 获取。

深入了解

评论

支持 Markdown