Skip to content

懒加载获取

默认情况下,useFetchuseAsyncData阻塞导航直到数据加载完成。懒加载模式下,数据在后台获取,不阻塞导航——页面立刻显示,数据加载完后自动更新。

为什么需要懒加载?

问题阻塞模式(默认)懒加载模式
页面切换慢等所有数据加载完才显示页面立刻显示
非关键数据拖慢首屏评论区、推荐等阻塞渲染先显示页面,后台加载
用户感知差点击链接后白屏等待立刻看到页面框架 + Loading
SEO 影响数据完整,SEO 好数据不完整,SEO 差

核心取舍

懒加载 = 更快的页面切换 换取 SEO 和首屏数据完整性

经验法则

页面核心内容(文章、商品)用阻塞加载,非关键内容(评论、推荐、侧边栏)用懒加载。

useLazyFetch

useLazyFetchuseFetch 的懒加载版本:

vue
<script setup>
const { data, pending, error } = useLazyFetch('/api/users')
</script>

<template>
  <div v-if="pending">加载中...</div>
  <div v-else-if="error">{{ error.message }}</div>
  <div v-else>
    <ul>
      <li v-for="user in data" :key="user.id">{{ user.name }}</li>
    </ul>
  </div>
</template>

等价于:

ts
const { data, pending, error } = await useFetch('/api/users', { lazy: true })

useLazyFetch vs useFetch({ lazy: true })

两者完全等价。useLazyFetch 是语法糖,语义更明确。推荐用 useLazyFetch,因为:

  • 代码意图更清晰——一眼就知道是懒加载
  • 不需要 await(懒加载不阻塞导航)

INFO

注意useLazyFetch 不需要 await 因为它不阻塞导航。加了 await 也没效果

useLazyAsyncData

useLazyAsyncDatauseAsyncData 的懒加载版本:

vue
<script setup>
const { data, pending, error } = useLazyAsyncData(
  'users',
  () => $fetch('/api/users')
)
</script>

<template>
  <div v-if="pending">加载中...</div>
  <div v-else>
    <ul>
      <li v-for="user in data" :key="user.id">{{ user.name }}</li>
    </ul>
  </div>
</template>

等价于:

ts
const { data, pending } = await useAsyncData('users', () => $fetch('/api/users'), { lazy: true })

什么时候用 useLazyAsyncData

当你的获取逻辑不是简单的 HTTP 请求时(如聚合多个 API、使用第三方 SDK),用 useLazyAsyncData 包裹并启用懒加载。

阻塞 vs 非阻塞——深入理解

阻塞模式(默认)

text
用户点击链接 → 等待数据加载 → 页面显示
                  ↑ 阻塞在这里
                  │ 服务端:等待 useFetch 完成 → 渲染完整 HTML
                  │ 客户端导航:显示 NuxtLoadingIndicator → 等待数据
  • 页面切换时有等待
  • 数据加载完才显示页面
  • SEO 友好(SSR 时数据已就绪,搜索引擎能抓到)
  • 用户体验:等待 → 完整内容(一气呵成)

非阻塞模式(lazy)

text
用户点击链接 → 页面立即显示 → 数据加载完成
                                    ↑ 显示 loading
                                    │ SSR 时:HTML 中不包含数据
                                    │ 客户端导航:立刻显示页面框架
  • 页面切换立刻完成
  • 先显示 loading 状态,数据加载后自动更新
  • SEO 不友好(SSR 时数据未就绪,搜索引擎抓不到)
  • 用户体验:立刻看到页面 → loading → 内容(渐进式)

SSR 时懒加载的数据去哪了?

  • 阻塞模式:服务端等待数据 → 嵌入 HTML → 客户端直接使用
  • 懒加载模式:服务端不等待 → HTML 中无数据 → 客户端 Hydration 后自行请求

这就是懒加载"SEO 不友好"的原因——搜索引擎抓到的 HTML 里没有数据。

何时使用懒加载

✅ 适合懒加载

场景原因示例
非关键数据不影响页面主体内容评论区、推荐列表
用户交互后加载需要用户操作才触发折叠面板内容、Tab 切换内容
不影响 SEO后台页面、需登录页面用户设置页、管理后台
加载时间长的数据避免阻塞整个页面大型报表、统计图表
个性化数据每个用户不同,不适合 SSR"为你推荐"、最近浏览

❌ 不适合懒加载

场景原因应该用
页面核心内容没有 SEO 数据搜索引擎不收录useFetch(阻塞)
SEO 重要的数据需要搜索引擎抓取useFetch(阻塞)
页面结构依赖的数据没有数据页面会变形useFetch(阻塞)
需要服务端鉴权的数据需要服务端验证身份useFetch + server: true

设置默认值

懒加载时 data 初始为 undefined,使用 default 设置默认值避免模板报错:

ts
// ❌ 不设 default:data 初始为 undefined
const { data } = useLazyFetch('/api/users')
// 模板中 v-for="user in data" → 报错(undefined 没有 .map)
ts
// ✅ 设置 default:data 初始为空数组
const { data } = useLazyFetch('/api/users', {
  default: () => [],  // data 初始为 [],v-for 不报错
})

default 的常见值

  • 列表数据:default: () => []
  • 单个对象:default: () => ({})
  • 计数等数字:default: () => 0
  • 字符串:default: () => ''
  • 布尔值:default: () => false

INFO

default 必须是函数default: () => []default: [] ❌。因为函数每次调用返回新引用,避免多个组件共享同一引用

与 ClientOnly 配合

对于完全不需要 SSR 的数据,可以配合 <ClientOnly> 使用:

vue
<template>
  <!-- 核心内容:阻塞加载 -->
  <article>
    <h1>{{ article.title }}</h1>
    <div v-html="article.content" />
  </article>

  <!-- 评论区:仅客户端渲染 + 懒加载 -->
  <ClientOnly>
    <LazyComments :article-id="article.id" />
    <template #fallback>
      <p>评论加载中...</p>
    </template>
  </ClientOnly>
</template>

ClientOnly + 懒加载 vs 单独懒加载

  • 单独 useLazyFetch:SSR 时仍会执行请求(只是不阻塞),HTML 中可能有空占位
  • ClientOnly:SSR 时完全不渲染组件,连请求都不发。适合依赖浏览器 API 的组件(如图表、地图)

完整示例——文章页

vue
<script setup>
const route = useRoute()
const id = route.params.id

// 核心数据:阻塞加载(SEO 重要)
const { data: article } = await useFetch(`/api/articles/${id}`)

// 评论区:懒加载(非关键,SEO 不需要)
const { data: comments, pending: commentsLoading } = useLazyFetch(
  `/api/articles/${id}/comments`,
  { default: () => [] }
)

// 相关推荐:懒加载(非关键)
const { data: related, pending: relatedLoading } = useLazyFetch(
  `/api/articles/${id}/related`,
  { default: () => [] }
)

// 阅读统计:仅客户端(依赖浏览器 API)
const { data: stats } = useLazyFetch(`/api/articles/${id}/stats`, {
  server: false,
  default: () => ({ views: 0, likes: 0 }),
})
</script>

<template>
  <article>
    <h1>{{ article.title }}</h1>
    <div v-html="article.content" />
    <p>阅读 {{ stats.views }} · 点赞 {{ stats.likes }}</p>
  </article>

  <!-- 评论区 -->
  <section>
    <h2>评论</h2>
    <div v-if="commentsLoading">加载评论中...</div>
    <div v-else>
      <Comment v-for="c in comments" :key="c.id" :comment="c" />
    </div>
  </section>

  <!-- 相关推荐 -->
  <section>
    <h2>相关文章</h2>
    <div v-if="relatedLoading">加载推荐中...</div>
    <div v-else>
      <ArticleCard v-for="r in related" :key="r.id" :article="r" />
    </div>
  </section>
</template>

这个示例展示了最佳实践

  1. 文章正文useFetch(阻塞)→ SEO + 首屏完整
  2. 评论和推荐useLazyFetch(懒加载)→ 不阻塞页面
  3. 阅读统计server: false → 仅客户端,避免 SSR 无意义请求
  4. 所有懒加载都设了 default → 避免 undefined 报错

懒加载组件

Nuxt 还支持组件级别的懒加载——只在组件首次使用时才加载代码:

vue
<template>
  <!-- 懒加载组件:只在首次渲染时加载 -->
  <LazyHeavyChart :data="chartData" />

  <!-- 普通组件:页面加载时就下载代码 -->
  <SimpleCard>...</SimpleCard>
</template>

懒加载组件 vs 懒加载数据

  • LazyXxx:懒加载组件代码(减少首屏 JS 体积)
  • useLazyFetch:懒加载数据(不阻塞页面切换)
  • 两者可以组合使用:懒加载组件 + 懒加载数据

常见问题

1. 懒加载导致 Hydration 不匹配?

懒加载时 data 初始为 undefined,如果服务端和客户端渲染结果不同,可能出现 Hydration 不匹配。解决方案:

ts
// ✅ 使用 default 提供一致的初始值
const { data } = useLazyFetch('/api/users', {
  default: () => [],
})

2. 懒加载的数据什么时候开始请求?

ts
// useLazyFetch 在组件 setup 时就开始请求
// 只是页面不会等待请求完成
const { data, pending } = useLazyFetch('/api/users')
// pending 立刻为 true,请求在后台进行

3. 懒加载和 server: false 的区别?

ts
// 懒加载:SSR 时请求但不等待,客户端 Hydration 后使用客户端请求的结果
const { data } = useLazyFetch('/api/data')

// server: false:SSR 时不请求,只在客户端请求
const { data } = await useFetch('/api/data', { server: false })

// 组合:SSR 时不请求 + 不阻塞
const { data } = useLazyFetch('/api/data', { server: false })

如何选择

  • 只需要"不阻塞" → useLazyFetch(默认 server: true,SSR 时仍请求)
  • SSR 时完全不需要请求 → server: false(如用户个人信息)
  • 两者都要 → useLazyFetch + server: false

知识脉络

text
useFetch → useAsyncData → $fetch

  └─→ 你在这里:懒加载获取

        ├─→ 相关:数据获取选项(lazy / server 详解)

        └─→ 相关:缓存与刷新

基于 Nuxt 4 官方文档整理编写