Nuxt.js缓存策略 #

一、缓存概述 #

Nuxt.js 提供了多层缓存机制来优化应用性能:

缓存类型 层级 说明
数据缓存 应用层 缓存 API 响应数据
路由缓存 服务端 缓存渲染的页面
静态资源缓存 CDN/浏览器 缓存静态文件
组件缓存 应用层 缓存组件渲染结果

二、数据缓存 #

2.1 useFetch缓存 #

vue
<script setup lang="ts">
const { data } = await useFetch('/api/users', {
  key: 'users-list',
  getCachedData(key) {
    return useNuxtData(key).data.value
  }
})
</script>

2.2 缓存时间控制 #

vue
<script setup lang="ts">
const CACHE_DURATION = 5 * 60 * 1000

const cacheStore = new Map<string, { data: any; timestamp: number }>()

const { data } = await useFetch('/api/users', {
  key: 'users-list',
  getCachedData(key) {
    const cached = cacheStore.get(key)
    
    if (cached && Date.now() - cached.timestamp < CACHE_DURATION) {
      return cached.data
    }
    
    return null
  },
  transform(data) {
    cacheStore.set('users-list', {
      data,
      timestamp: Date.now()
    })
    return data
  }
})
</script>

2.3 useAsyncData缓存 #

vue
<script setup lang="ts">
const { data } = await useAsyncData(
  'articles',
  () => $fetch('/api/articles'),
  {
    transform: (data) => {
      return {
        ...data,
        cachedAt: Date.now()
      }
    }
  }
)
</script>

2.4 清除缓存 #

vue
<script setup lang="ts">
const clearCache = () => {
  clearNuxtData('users-list')
}

const clearAllCache = () => {
  clearNuxtData()
}
</script>

三、路由缓存 #

3.1 配置路由缓存 #

nuxt.config.ts

typescript
export default defineNuxtConfig({
  routeRules: {
    '/': { cache: { maxAge: 60 * 60 } },
    '/blog/**': { cache: { maxAge: 60 * 60 * 24 } },
    '/api/users': { cache: { maxAge: 60 } },
    '/admin/**': { cache: false }
  }
})

3.2 路由规则选项 #

typescript
export default defineNuxtConfig({
  routeRules: {
    '/blog/[id]': {
      cache: {
        maxAge: 60 * 60 * 24,
        staleMaxAge: 60 * 60,
        swr: true,
        vary: ['Accept-Language']
      }
    }
  }
})

3.3 ISR增量静态再生 #

typescript
export default defineNuxtConfig({
  routeRules: {
    '/blog/**': {
      isr: 3600
    },
    '/products/**': {
      isr: {
        allowQuery: ['category', 'sort']
      }
    }
  }
})

四、API缓存 #

4.1 Nitro缓存 #

server/api/users.ts

typescript
export default defineCachedEventHandler(
  async () => {
    const users = await fetchUsers()
    return { users }
  },
  {
    maxAge: 60 * 60,
    swr: true,
    varies: ['Authorization']
  }
)

4.2 缓存选项 #

typescript
export default defineCachedEventHandler(
  async (event) => {
    return { data: 'cached response' }
  },
  {
    maxAge: 60 * 60,
    staleMaxAge: 60 * 60 * 24,
    swr: true,
    base: 'db',
    shouldBypassCache: (event) => {
      return getQuery(event).noCache === 'true'
    },
    getKey: (event) => {
      const id = getRouterParam(event, 'id')
      return `user-${id}`
    }
  }
)

4.3 条件缓存 #

typescript
export default defineCachedEventHandler(
  async (event) => {
    const id = getRouterParam(event, 'id')
    const user = await fetchUser(id)
    return { user }
  },
  {
    maxAge: 60 * 60,
    shouldBypassCache: (event) => {
      const user = event.context.user
      return user?.role === 'admin'
    }
  }
)

五、静态资源缓存 #

5.1 配置静态资源缓存 #

nuxt.config.ts

typescript
export default defineNuxtConfig({
  nitro: {
    compressPublicAssets: true
  }
})

5.2 自定义缓存头 #

server/middleware/headers.ts

typescript
export default defineEventHandler((event) => {
  const url = getRequestURL(event)
  
  if (url.pathname.startsWith('/_nuxt/')) {
    setHeader(event, 'Cache-Control', 'public, max-age=31536000, immutable')
  }
  
  if (url.pathname.match(/\.(jpg|jpeg|png|gif|webp|svg)$/)) {
    setHeader(event, 'Cache-Control', 'public, max-age=86400')
  }
})

六、SWR策略 #

6.1 Stale-While-Revalidate #

typescript
export default defineCachedEventHandler(
  async () => {
    const data = await fetchData()
    return data
  },
  {
    maxAge: 60,
    staleMaxAge: 3600,
    swr: true
  }
)

6.2 客户端SWR #

composables/useSWR.ts

typescript
export const useSWR = <T>(
  key: string,
  fetcher: () => Promise<T>,
  options: {
    refreshInterval?: number
    revalidateOnFocus?: boolean
  } = {}
) => {
  const data = ref<T | null>(null)
  const error = ref<Error | null>(null)
  const isValidating = ref(false)
  
  const cached = useNuxtData(key)
  
  if (cached.data.value) {
    data.value = cached.data.value
  }
  
  const revalidate = async () => {
    isValidating.value = true
    
    try {
      const result = await fetcher()
      data.value = result
      cached.data.value = result
    } catch (e) {
      error.value = e as Error
    } finally {
      isValidating.value = false
    }
  }
  
  if (!cached.data.value) {
    revalidate()
  }
  
  if (options.refreshInterval) {
    setInterval(revalidate, options.refreshInterval)
  }
  
  if (options.revalidateOnFocus && import.meta.client) {
    window.addEventListener('focus', revalidate)
  }
  
  return {
    data,
    error,
    isValidating,
    revalidate
  }
}

七、缓存失效 #

7.1 手动失效 #

vue
<script setup lang="ts">
const { data, refresh } = await useFetch('/api/users')

const invalidateCache = async () => {
  clearNuxtData('users-list')
  await refresh()
}
</script>

7.2 基于时间的失效 #

vue
<script setup lang="ts">
const CACHE_TTL = 5 * 60 * 1000

const { data, refresh } = await useFetch('/api/users', {
  transform: (data) => ({
    ...data,
    cachedAt: Date.now()
  })
})

const checkCache = () => {
  if (data.value?.cachedAt && Date.now() - data.value.cachedAt > CACHE_TTL) {
    refresh()
  }
}

onMounted(() => {
  checkCache()
})
</script>

7.3 事件驱动失效 #

server/api/posts.post.ts

typescript
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  
  const post = await createPost(body)
  
  await useStorage('cache').removeItem('nitro:handlers:/api/posts')
  
  return { post }
})

八、缓存存储 #

8.1 内存缓存 #

typescript
export default defineCachedEventHandler(
  async () => {
    return { data: 'cached in memory' }
  },
  {
    base: 'memory',
    maxAge: 60 * 60
  }
)

8.2 Redis缓存 #

nuxt.config.ts

typescript
export default defineNuxtConfig({
  nitro: {
    storage: {
      cache: {
        driver: 'redis',
        url: process.env.REDIS_URL
      }
    }
  }
})

8.3 文件系统缓存 #

typescript
export default defineNuxtConfig({
  nitro: {
    storage: {
      cache: {
        driver: 'fs',
        base: './.cache'
      }
    }
  }
})

九、缓存最佳实践 #

9.1 缓存策略选择 #

数据类型 推荐策略 缓存时间
静态内容 ISR 24小时+
博客文章 SWR 1小时
用户数据 客户端缓存 5分钟
实时数据 不缓存 -

9.2 缓存键设计 #

typescript
const getCacheKey = (event: H3Event) => {
  const user = event.context.user
  const query = getQuery(event)
  
  return `data:${user?.id || 'anonymous'}:${JSON.stringify(query)}`
}

export default defineCachedEventHandler(
  async () => {
    return { data: 'personalized' }
  },
  {
    getKey: getCacheKey,
    maxAge: 60
  }
)

9.3 缓存预热 #

server/plugins/warmup.ts

typescript
export default defineNitroPlugin(async () => {
  const popularPages = ['/', '/blog', '/products']
  
  for (const page of popularPages) {
    await $fetch(page).catch(() => {})
  }
})

十、完整示例 #

10.1 博客缓存系统 #

nuxt.config.ts

typescript
export default defineNuxtConfig({
  routeRules: {
    '/': { isr: 3600 },
    '/blog': { isr: 1800 },
    '/blog/**': { isr: 7200 },
    '/api/blog/**': { cache: { maxAge: 3600, swr: true } }
  },
  
  nitro: {
    storage: {
      cache: {
        driver: 'redis',
        url: process.env.REDIS_URL
      }
    }
  }
})

server/api/blog/[slug].ts

typescript
export default defineCachedEventHandler(
  async (event) => {
    const slug = getRouterParam(event, 'slug')
    
    const article = await fetchArticle(slug)
    
    if (!article) {
      throw createError({
        statusCode: 404,
        message: 'Article not found'
      })
    }
    
    return { article }
  },
  {
    maxAge: 60 * 60,
    staleMaxAge: 60 * 60 * 24,
    swr: true,
    getKey: (event) => {
      const slug = getRouterParam(event, 'slug')
      return `article:${slug}`
    },
    varies: ['Accept-Language']
  }
)

pages/blog/[slug].vue

vue
<script setup lang="ts">
const route = useRoute()

const { data: article, refresh } = await useFetch(`/api/blog/${route.params.slug}`, {
  key: `article-${route.params.slug}`,
  getCachedData(key) {
    const cached = useNuxtData(key)
    const data = cached.data.value
    
    if (data?.cachedAt) {
      const age = Date.now() - data.cachedAt
      if (age < 5 * 60 * 1000) {
        return data
      }
    }
    
    return null
  },
  transform: (data) => ({
    ...data,
    cachedAt: Date.now()
  })
})

const invalidateCache = async () => {
  clearNuxtData(`article-${route.params.slug}`)
  await refresh()
}
</script>

十一、总结 #

本章介绍了 Nuxt.js 缓存策略:

  • 数据缓存使用 useFetchuseAsyncData
  • 路由缓存配置 routeRules
  • API 缓存使用 defineCachedEventHandler
  • SWR 策略实现快速响应
  • 多种缓存存储选择
  • 缓存失效和预热

合理的缓存策略可以显著提升应用性能,下一章我们将学习状态管理。

最后更新:2026-03-28