Skip to content
On this page

hono-cache

简介

hono-cache 是 Hono 框架的缓存中间件,它提供了简单而强大的方式来缓存 API 响应,减少重复计算和数据库查询,提高应用性能。该中间件支持多种缓存策略和存储选项。

安装

bash
npm install hono @hono/cache

基本使用

typescript
import { Hono } from 'hono'
import { cache } from '@hono/cache'

const app = new Hono()

// 使用默认配置的缓存中间件
app.get(
  '/api/products',
  cache({
    cacheName: 'api-cache', // 缓存名称
    cacheControl: 'max-age=60', // 缓存控制,60秒
  }),
  async (c) => {
    // 这个处理函数只有在缓存未命中时才会执行
    console.log('缓存未命中,获取产品数据...')

    // 模拟数据库查询
    const products = await fetchProductsFromDatabase()

    return c.json(products)
  },
)

app.listen(3000)

// 模拟数据库查询函数
async function fetchProductsFromDatabase() {
  // 在实际应用中,这里会是数据库查询
  return [
    { id: 1, name: '产品 A', price: 99.99 },
    { id: 2, name: '产品 B', price: 149.99 },
    { id: 3, name: '产品 C', price: 199.99 },
  ]
}

高级配置

typescript
import { Hono } from 'hono'
import { cache } from '@hono/cache'

const app = new Hono()

// 自定义缓存配置
app.get(
  '/api/users/:id',
  cache({
    // 缓存名称
    cacheName: 'users-cache',

    // 缓存控制
    cacheControl: 'max-age=300, s-maxage=600', // 浏览器缓存5分钟,CDN缓存10分钟

    // 缓存键生成函数
    cacheKey: (c) => {
      // 使用路径和用户ID作为缓存键
      const id = c.req.param('id')
      return `user-${id}-${c.req.query('version') || 'latest'}`
    },

    // 是否等待缓存写入完成
    wait: true,

    // 缓存条件函数
    shouldCache: (c) => {
      // 只缓存成功的响应
      return c.res.status === 200
    },

    // 缓存过期时间(毫秒)
    ttl: 60 * 1000, // 1分钟

    // 缓存变体
    vary: ['Accept-Language', 'Accept-Encoding'],
  }),
  async (c) => {
    const id = c.req.param('id')
    const user = await fetchUserById(id)

    if (!user) {
      return c.json({ error: '用户不存在' }, 404)
    }

    return c.json(user)
  },
)

// 使用不同配置的缓存中间件
app.get(
  '/api/products/:category',
  cache({
    cacheName: 'products-cache',
    // 使用 stale-while-revalidate 策略
    cacheControl: 'max-age=60, stale-while-revalidate=600',
    // 根据类别和查询参数生成缓存键
    cacheKey: (c) => {
      const category = c.req.param('category')
      const query = new URLSearchParams(c.req.query())
      return `products-${category}-${query.toString()}`
    },
  }),
  async (c) => {
    const category = c.req.param('category')
    const page = parseInt(c.req.query('page') || '1')
    const limit = parseInt(c.req.query('limit') || '10')

    const products = await fetchProductsByCategory(category, page, limit)

    return c.json({
      category,
      page,
      limit,
      products,
    })
  },
)

app.listen(3000)

// 模拟数据获取函数
async function fetchUserById(id: string) {
  // 模拟数据库查询延迟
  await new Promise((resolve) => setTimeout(resolve, 100))

  // 模拟用户数据
  const users = {
    '1': { id: 1, name: '张三', email: 'zhangsan@example.com' },
    '2': { id: 2, name: '李四', email: 'lisi@example.com' },
  }

  return users[id] || null
}

async function fetchProductsByCategory(category: string, page: number, limit: number) {
  // 模拟数据库查询延迟
  await new Promise((resolve) => setTimeout(resolve, 200))

  // 模拟产品数据
  const allProducts = {
    electronics: [
      { id: 1, name: '智能手机', price: 3999 },
      { id: 2, name: '笔记本电脑', price: 5999 },
      { id: 3, name: '平板电脑', price: 2999 },
    ],
    clothing: [
      { id: 4, name: 'T恤', price: 99 },
      { id: 5, name: '牛仔裤', price: 199 },
      { id: 6, name: '外套', price: 299 },
    ],
  }

  const products = allProducts[category] || []
  const start = (page - 1) * limit
  const end = start + limit

  return products.slice(start, end)
}

与内存缓存集成

typescript
import { Hono } from 'hono'
import { cache } from '@hono/cache'

const app = new Hono()

// 创建内存缓存存储
const memoryStore = new Map()

app.get(
  '/api/stats',
  cache({
    cacheName: 'stats-cache',
    cacheControl: 'max-age=60',
    // 使用自定义存储
    storage: {
      async get(key: string) {
        const cached = memoryStore.get(key)
        if (!cached) return null

        const { value, expires } = cached
        if (expires < Date.now()) {
          memoryStore.delete(key)
          return null
        }

        return value
      },
      async set(key: string, value: any, ttl: number) {
        const expires = Date.now() + ttl
        memoryStore.set(key, { value, expires })
      },
      async delete(key: string) {
        memoryStore.delete(key)
      },
    },
  }),
  async (c) => {
    // 模拟获取统计数据(耗时操作)
    const stats = await generateStats()
    return c.json(stats)
  },
)

app.listen(3000)

// 模拟统计数据生成
async function generateStats() {
  // 模拟耗时计算
  await new Promise((resolve) => setTimeout(resolve, 500))

  return {
    totalUsers: 10000,
    activeUsers: 5000,
    totalProducts: 1000,
    timestamp: new Date().toISOString(),
  }
}

要保持清醒 永远不抱有意外的幻想 凭空的期待最要命