Vue Academy · 课时

Vue Suspense 组件

使用带有 default 和 #fallback 插槽的 、异步组件加载,以及嵌套 Suspense

第 1 / 4 课13 个步骤

Vue Suspense 组件 是 CoddyKit 上的免费 Vue Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Vue Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Vue Academy 课程共包含 4 节课。

Suspense 解决什么问题

Suspense 是 Vue 内置的组件,用于协调具有 异步依赖的组件的加载状态。

没有它时,每个异步组件都要管理自己的加载指示器。借助 Suspense,您可以声明一个边界,等待所有嵌套的异步工作完成,并在一切就绪前显示一个统一的 fallback。

两个插槽:默认和 fallback

Suspense 提供两个具名插槽:

  • 默认——实际内容,其中可以包含异步组件或异步的 setup()
  • fallback——默认插槽解析期间要显示的内容

所有异步依赖完成后,Vue 会用默认内容替换 fallback。

<template>
  <Suspense>
    <template #default>
      <UserDashboard />
    </template>
    <template #fallback>
      <div class="spinner">Loading dashboard...</div>
    </template>
  </Suspense>
</template>

异步设置函数作为依赖

当组件的 setup() 函数声明为 async 并等待某项操作时,该组件就会成为异步依赖。Vue 会跟踪返回的承诺。

<script setup> 中的等价写法,是直接在代码块顶层使用 await。

<script setup>
import { ref } from 'vue'

// Top-level await makes this an async dependency of Suspense
const res = await fetch('/api/profile')
const profile = ref(await res.json())
</script>

<template>
  <h1>Welcome, {{ profile.name }}</h1>
</template>

异步组件也会被跟踪

使用 defineAsyncComponent 创建的组件也会由 Suspense 跟踪。动态的 import() 完成解析前,fallback 会一直显示。

这样,您无需为每个组件单独编写加载界面,也能对大型组件进行代码拆分。

<script setup>
import { defineAsyncComponent } from 'vue'

const Chart = defineAsyncComponent(() =>
  import('./HeavyChart.vue')
)
</script>

<template>
  <Suspense>
    <Chart />
    <template #fallback>Preparing chart...</template>
  </Suspense>
</template>

多个异步子组件共同等待

如果默认插槽包含多个异步依赖,Suspense 会等待所有依赖解析完成后才显示内容。

这样可以避免组件逐个出现导致的布局偏移。

<template>
  <Suspense>
    <div>
      <UserStats />   <!-- async setup -->
      <RecentOrders /> <!-- async setup -->
      <Recommendations /> <!-- async component -->
    </div>
    <template #fallback>
      <PageSkeleton />
    </template>
  </Suspense>
</template>

使用 onErrorCaptured 捕获拒绝

如果异步 setup() 被拒绝(例如获取数据失败),错误会向上传播到父组件中最近的 onErrorCaptured 钩子。

Suspense 本身不会渲染错误,因此需要将它与错误边界配合使用。

<script setup>
import { ref, onErrorCaptured } from 'vue'

const error = ref(null)

onErrorCaptured((err) => {
  error.value = err
  return false // stop the error from propagating further
})
</script>

错误边界 + 异步挂起模式

生产环境中常见的模式是将 Suspense 包裹在错误边界组件中。onErrorCaptured 返回 false 来停止传播,并改为渲染错误界面。

<script setup>
import { ref, onErrorCaptured } from 'vue'
const err = ref(null)
onErrorCaptured((e) => { err.value = e; return false })
</script>

<template>
  <p v-if="err">Failed to load: {{ err.message }}</p>
  <Suspense v-else>
    <ProfilePanel />
    <template #fallback>Loading...</template>
  </Suspense>
</template>

pending 和 resolve 事件

Suspense 会发出您可以监听的生命周期事件:

  • @pending — 进入 pending(loading)状态时触发
  • @resolve — 默认插槽完成解析时触发
  • @fallback — 显示后备内容时触发

这对于数据分析或进度条很有用。

<template>
  <Suspense @pending="onPending" @resolve="onResolve">
    <RouteView />
    <template #fallback>Loading route...</template>
  </Suspense>
</template>

timeout 属性:延迟显示后备内容

timeout 属性(以毫秒为单位)会延迟显示后备内容。如果异步操作的解析速度快于超时时间,就不会显示后备内容。

这样可以避免快速响应时加载指示器一闪而过。

<template>
  <!-- Only show fallback if loading exceeds 200ms -->
  <Suspense :timeout="200">
    <FastPanel />
    <template #fallback>Loading...</template>
  </Suspense>
</template>

异步挂起与路由器及过渡效果

Suspense 可以与 RouterView 和 Transition 组合,为异步路由切换添加动画。顺序很重要:Transition 包裹 Suspense,而 Suspense 再包裹路由组件。

<template>
  <RouterView v-slot="{ Component }">
    <Transition mode="out-in">
      <Suspense>
        <component :is="Component" />
        <template #fallback>Loading page...</template>
      </Suspense>
    </Transition>
  </RouterView>
</template>

何时 NOT 使用异步挂起

如果数据会频繁重新获取,而组件始终处于挂载状态,请避免使用 Suspense——它只管理初始异步解析。对于持续的加载状态(分页、重新获取),请改用普通的响应式 loading 引用。

<script setup>
import { ref } from 'vue'
const loading = ref(false)
async function loadMore() {
  loading.value = true
  await fetchNextPage()
  loading.value = false
}
</script>

快速检查

检验您对异步挂起的理解。

回顾

您已经学习了 Suspense 如何协调异步依赖:

  • 默认插槽容纳异步内容(异步 setup 或异步组件)
  • 后备内容插槽在加载期间显示
  • 它会等待所有异步子组件完成后才显示内容
  • 拒绝会冒泡到父级边界中的 onErrorCaptured
  • timeout 可避免加载指示器闪现;@resolve 等事件有助于数据分析
免费开始

用 AI 导师学习 HTML — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
42
课程
156

常见问题解答

「Vue Suspense 组件」课时是免费的吗?

是的 — 「Vue Suspense 组件」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Vue Academy 课程的其余内容,请升级到 CoddyKit PRO。 Vue Academy 课程共包含 4 节课。

「Vue Suspense 组件」这节课中我会学到什么?

使用带有 default 和 #fallback 插槽的 、异步组件加载,以及嵌套 Suspense 你通过在浏览器中直接运行的动手代码来练习 Vue Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Vue Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Vue Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「Vue Suspense 组件」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Vue Academy 课中编写并运行代码吗?

能。每节 Vue Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Vue Suspense 组件
  2. 使用 defineAsyncComponent 加载异步组件
  3. 使用 renderToWebStream 实现流式 SSR
  4. 延迟 Hydration 策略
← 返回 Vue Academy