全书目录

第三十四篇:Hono 完全指南 —— 边缘轻骑哨站宇宙

第五站:路由是驿道图,不是控制器坟场

2 分钟 497 字 第 641 / 962 个阅读单元

很多人第一次上手 Hono,会自然想把它写成“Express 风格 + 一堆控制器文件”。

但 Hono 官方最佳实践一直非常明确:能不做 RoR 式 Controller,就尽量别做。

原因不是玄学,而是类型推断会变差

#推荐写法:路径旁边就写处理逻辑

ts
import { Hono } from 'hono'

const app = new Hono()

app.get('/outposts/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ id }, 200)
})

这时 id 的类型推断是顺的。

#不太推荐:把 handler 提前抽成“万能控制器”

ts
import type { Context } from 'hono'

const getOutpost = (c: Context) => {
  const id = c.req.param('id') // 类型推断会变得不舒服
  return c.json({ id })
}

Hono 的气质不是“层层包一圈”,而是“路牌和处理动作尽量挨着”。

#模块化正确姿势:用 app.route()

当哨站变多时,不要把所有驿道都堆在一个文件里。正确拆法是子哨站实例化,再挂载

ts
// routes/outposts.ts
import { Hono } from 'hono'

export const outposts = new Hono()
  .get('/', (c) => c.json({ items: ['north-1', 'west-2'] }, 200))
  .get('/:id', (c) => c.json({ id: c.req.param('id') }, 200))
ts
// app.ts
import { Hono } from 'hono'
import { outposts } from './routes/outposts'

export const app = new Hono().route('/outposts', outposts)

#一个很容易踩的坑:route() 挂载顺序

app.route('/x', childApp) 不是“引用绑定”,更像“把当时已经建好的驿道抄一份贴到总图上”。

所以这种顺序可能出问题:

ts
const child = new Hono()
const parent = new Hono()

parent.route('/child', child)

child.get('/hello', (c) => c.text('hello'))

你以为挂上了,其实总图挂的是“当时还没写完的 child”。

所以经验法则很简单:

先把子哨站画完整,再挂到总哨站。

#Hono 路由并不弱

它不只是简单的 /users/:id

你还能写:

ts
app.get('/patrol/:zone?', (c) => c.text('可选参数'))

app.get('/logs/:date{[0-9]+}', (c) => {
  return c.text(`日志日期: ${c.req.param('date')}`)
})

这说明 Hono 不是“因为轻,所以简陋”;它是“轻,但足够锋利”。