nuxt4-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Nuxt 4 Patterns

Nuxt 4 应用模式

Use when building or debugging Nuxt 4 apps with SSR, hybrid rendering, route rules, or page-level data fetching.
适用于构建或调试采用SSR、混合渲染、路由规则或页面级数据获取的Nuxt 4应用时使用。

When to Activate

适用场景

  • Hydration mismatches between server HTML and client state
  • Route-level rendering decisions such as prerender, SWR, ISR, or client-only sections
  • Performance work around lazy loading, lazy hydration, or payload size
  • Page or component data fetching with
    useFetch
    ,
    useAsyncData
    , or
    $fetch
  • Nuxt routing issues tied to route params, middleware, or SSR/client differences
  • 服务器端HTML与客户端状态之间的水合不匹配问题
  • 路由级别的渲染决策,如预渲染(prerender)、SWR、ISR或仅客户端渲染区域
  • 围绕懒加载、延迟水合或 payload 大小的性能优化工作
  • 使用
    useFetch
    useAsyncData
    $fetch
    进行页面或组件的数据获取
  • 与路由参数、中间件或SSR/客户端差异相关的Nuxt路由问题

Hydration Safety

水合安全性

  • Keep the first render deterministic. Do not put
    Date.now()
    ,
    Math.random()
    , browser-only APIs, or storage reads directly into SSR-rendered template state.
  • Move browser-only logic behind
    onMounted()
    ,
    import.meta.client
    ,
    ClientOnly
    , or a
    .client.vue
    component when the server cannot produce the same markup.
  • Use Nuxt's
    useRoute()
    composable, not the one from
    vue-router
    .
  • Do not use
    route.fullPath
    to drive SSR-rendered markup. URL fragments are client-only, which can create hydration mismatches.
  • Treat
    ssr: false
    as an escape hatch for truly browser-only areas, not a default fix for mismatches.
  • 确保首次渲染的确定性。不要将
    Date.now()
    Math.random()
    、仅浏览器可用的API或存储读取操作直接放入SSR渲染的模板状态中。
  • 当服务器无法生成相同标记时,将仅浏览器逻辑移至
    onMounted()
    import.meta.client
    ClientOnly
    组件或
    .client.vue
    组件中。
  • 使用Nuxt提供的
    useRoute()
    组合式函数,而非
    vue-router
    中的同名函数。
  • 不要使用
    route.fullPath
    来驱动SSR渲染的标记。URL片段仅在客户端可用,这可能导致水合不匹配。
  • ssr: false
    视为纯浏览器区域的应急方案,而非解决不匹配问题的默认方法。

Data Fetching

数据获取

  • Prefer
    await useFetch()
    for SSR-safe API reads in pages and components. It forwards server-fetched data into the Nuxt payload and avoids a second fetch on hydration.
  • Use
    useAsyncData()
    when the fetcher is not a simple
    $fetch()
    call, when you need a custom key, or when you are composing multiple async sources.
  • Give
    useAsyncData()
    a stable key for cache reuse and predictable refresh behavior.
  • Keep
    useAsyncData()
    handlers side-effect free. They can run during SSR and hydration.
  • Use
    $fetch()
    for user-triggered writes or client-only actions, not top-level page data that should be hydrated from SSR.
  • Use
    lazy: true
    ,
    useLazyFetch()
    , or
    useLazyAsyncData()
    for non-critical data that should not block navigation. Handle
    status === 'pending'
    in the UI.
  • Use
    server: false
    only for data that is not needed for SEO or the first paint.
  • Trim payload size with
    pick
    and prefer shallower payloads when deep reactivity is unnecessary.
ts
const route = useRoute()

const { data: article, status, error, refresh } = await useAsyncData(
  () => `article:${route.params.slug}`,
  () => $fetch(`/api/articles/${route.params.slug}`),
)

const { data: comments } = await useFetch(`/api/articles/${route.params.slug}/comments`, {
  lazy: true,
  server: false,
})
  • 在页面和组件中优先使用
    await useFetch()
    进行SSR安全的API读取。它会将服务器端获取的数据转发到Nuxt payload中,避免水合时再次获取数据。
  • 当数据获取逻辑不是简单的
    $fetch()
    调用、需要自定义key或组合多个异步数据源时,使用
    useAsyncData()
  • useAsyncData()
    设置稳定的key,以实现缓存复用和可预测的刷新行为。
  • 保持
    useAsyncData()
    的处理函数无副作用。它们可能会在SSR和水合过程中运行。
  • 使用
    $fetch()
    处理用户触发的写入操作或仅客户端的操作,而非用于需要从SSR水合的顶级页面数据。
  • 对于不需要阻塞导航的非关键数据,使用
    lazy: true
    useLazyFetch()
    useLazyAsyncData()
    。在UI中处理
    status === 'pending'
    状态。
  • 仅当数据不需要用于SEO或首次绘制时,才使用
    server: false
  • 使用
    pick
    方法精简payload大小,当不需要深度响应式时,优先使用结构更扁平的payload。
ts
const route = useRoute()

const { data: article, status, error, refresh } = await useAsyncData(
  () => `article:${route.params.slug}`,
  () => $fetch(`/api/articles/${route.params.slug}`),
)

const { data: comments } = await useFetch(`/api/articles/${route.params.slug}/comments`, {
  lazy: true,
  server: false,
})

Route Rules

路由规则

Prefer
routeRules
in
nuxt.config.ts
for rendering and caching strategy:
ts
export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
    '/products/**': { swr: 3600 },
    '/blog/**': { isr: true },
    '/admin/**': { ssr: false },
    '/api/**': { cache: { maxAge: 60 * 60 } },
  },
})
  • prerender
    : static HTML at build time
  • swr
    : serve cached content and revalidate in the background
  • isr
    : incremental static regeneration on supported platforms
  • ssr: false
    : client-rendered route
  • cache
    or
    redirect
    : Nitro-level response behavior
Pick route rules per route group, not globally. Marketing pages, catalogs, dashboards, and APIs usually need different strategies.
优先在
nuxt.config.ts
中使用
routeRules
配置渲染和缓存策略:
ts
export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
    '/products/**': { swr: 3600 },
    '/blog/**': { isr: true },
    '/admin/**': { ssr: false },
    '/api/**': { cache: { maxAge: 60 * 60 } },
  },
})
  • prerender
    : 构建时生成静态HTML
  • swr
    : 提供缓存内容并在后台重新验证
  • isr
    : 在支持的平台上进行增量静态再生
  • ssr: false
    : 客户端渲染路由
  • cache
    redirect
    : Nitro级别的响应行为
为每个路由组选择合适的路由规则,而非全局统一配置。营销页面、商品目录、仪表板和API通常需要不同的策略。

Lazy Loading and Performance

懒加载与性能优化

  • Nuxt already code-splits pages by route. Keep route boundaries meaningful before micro-optimizing component splits.
  • Use the
    Lazy
    prefix to dynamically import non-critical components.
  • Conditionally render lazy components with
    v-if
    so the chunk is not loaded until the UI actually needs it.
  • Use lazy hydration for below-the-fold or non-critical interactive UI.
vue
<template>
  <LazyRecommendations v-if="showRecommendations" />
  <LazyProductGallery hydrate-on-visible />
</template>
  • For custom strategies, use
    defineLazyHydrationComponent()
    with a visibility or idle strategy.
  • Nuxt lazy hydration works on single-file components. Passing new props to a lazily hydrated component will trigger hydration immediately.
  • Use
    NuxtLink
    for internal navigation so Nuxt can prefetch route components and generated payloads.
  • Nuxt已经按路由进行代码分割。在对组件分割进行微优化之前,先确保路由边界的合理性。
  • 使用
    Lazy
    前缀动态导入非关键组件。
  • 结合
    v-if
    条件渲染懒加载组件,确保代码块仅在UI实际需要时才加载。
  • 对首屏以下或非关键的交互式UI使用延迟水合。
vue
<template>
  <LazyRecommendations v-if="showRecommendations" />
  <LazyProductGallery hydrate-on-visible />
</template>
  • 对于自定义策略,使用
    defineLazyHydrationComponent()
    并结合可见性或空闲状态策略。
  • Nuxt的延迟水合功能适用于单文件组件。向延迟水合的组件传递新props会立即触发水合。
  • 使用
    NuxtLink
    进行内部导航,以便Nuxt可以预取路由组件和生成的payload。

Review Checklist

检查清单

  • First SSR render and hydrated client render produce the same markup
  • Page data uses
    useFetch
    or
    useAsyncData
    , not top-level
    $fetch
  • Non-critical data is lazy and has explicit loading UI
  • Route rules match the page's SEO and freshness requirements
  • Heavy interactive islands are lazy-loaded or lazily hydrated
  • SSR首次渲染和水合后的客户端渲染生成相同的标记
  • 页面数据使用
    useFetch
    useAsyncData
    ,而非顶级
    $fetch
  • 非关键数据采用懒加载方式,并配有明确的加载状态UI
  • 路由规则与页面的SEO和内容新鲜度需求匹配
  • 大型交互式独立区块采用懒加载或延迟水合