vue3中suspense的用法以及使用场景

Vue3 Suspense 完整指南

1. 基本概念

Suspense 是 Vue3 提供的一个内置组件,用于处理异步组件和异步数据加载。它可以在等待异步内容加载完成时显示加载状态,并处理加载过程中可能发生的错误。

1.1 基本语法

<template>
  <Suspense>
    <!-- 异步内容 -->
    <template #default>
      <async-component />
    </template>
    
    <!-- 加载状态 -->
    <template #fallback>
      <loading-spinner />
    </template>
  </Suspense>
</template>

2. 常见使用场景

2.1 异步组件加载

<!-- AsyncPage.vue -->
<script setup>
// 异步组件
const AsyncComponent = defineAsyncComponent(() =>
  import('./components/HeavyComponent.vue')
)
</script>

<template>
  <Suspense>
    <template #default>
      <AsyncComponent />
    </template>
    <template #fallback>
      <div class="loading">加载中...</div>
    </template>
  </Suspense>
</template>

2.2 异步数据获取

<!-- UserProfile.vue -->
<script setup>
import { ref } from 'vue'

// 异步 setup
async function setup() {
  const response = await fetch('/api/user')
  const user = await response.json()
  return { user }
}

const { user } = await setup()
</script>

<template>
  <div class="user-profile">
    <h2>{{ user.name }}</h2>
    <p>{{ user.email }}</p>
  </div>
</template>

<!-- 父组件 -->
<template>
  <Suspense>
    <template #default>
      <user-profile />
    </template>
    <template #fallback>
      <skeleton-loader />
    </template>
  </Suspense>
</template>

2.3 多个异步依赖

<!-- Dashboard.vue -->
<script setup>
// 多个异步数据获取
const userData = await fetch('/api/user').then(r => r.json())
const postsData = await fetch('/api/posts').then(r => r.json())
const analyticsData = await fetch('/api/analytics').then(r => r.json())
</script>

<template>
  <div class="dashboard">
    <user-info :user="userData" />
    <posts-list :posts="postsData" />
    <analytics-chart :data="analyticsData" />
  </div>
</template>

<!-- 父组件 -->
<template>
  <Suspense>
    <template #default>
      <dashboard />
    </template>
    <template #fallback>
      <div class="loading-dashboard">
        <loading-spinner />
        <p>加载仪表板...</p>
      </div>
    </template>
  </Suspense>
</template>

3. 高级用法

3.1 错误处理

<!-- ErrorBoundary.vue -->
<script setup>
import { ref, onErrorCaptured } from 'vue'

const error = ref(null)

onErrorCaptured((e) => {
  error.value = e
  return false // 阻止错误继续传播
})
</script>

<template>
  <div class="error-boundary">
    <template v-if="error">
      <div class="error-message">
        <h3>出错了!</h3>
        <p>{{ error.message }}</p>
        <button @click="error = null">重试</button>
      </div>
    </template>
    <template v-else>
      <slot></slot>
    </template>
  </div>
</template>

<!-- 使用错误边界 -->
<template>
  <error-boundary>
    <Suspense>
      <template #default>
        <async-component />
      </template>
      <template #fallback>
        <loading-spinner />
      </template>
    </Suspense>
  </error-boundary>
</template>

3.2 嵌套 Suspense

<!-- NestedAsync.vue -->
<template>
  <Suspense>
    <template #default>
      <div class="nested">
        <async-parent>
          <Suspense>
            <template #default>
              <async-child />
            </template>
            <template #fallback>
              <p>加载子组件...</p>
            </template>
          </Suspense>
        </async-parent>
      </div>
    </template>
    <template #fallback>
      <p>加载父组件...</p>
    </template>
  </Suspense>
</template>

3.3 动态组件切换

<!-- DynamicAsync.vue -->
<script setup>
import { ref, defineAsyncComponent } from 'vue'

const currentTab = ref('tab1')

const tabs = {
  tab1: defineAsyncComponent(() => import('./tabs/Tab1.vue')),
  tab2: defineAsyncComponent(() => import('./tabs/Tab2.vue')),
  tab3: defineAsyncComponent(() => import('./tabs/Tab3.vue'))
}
</script>

<template>
  <div class="tabs">
    <button
      v-for="(_, tab) in tabs"
      :key="tab"
      @click="currentTab = tab"
    >
      {{ tab }}
    </button>
    
    <Suspense>
      <template #default>
        <component :is="tabs[currentTab]" />
      </template>
      <template #fallback>
        <div class="tab-loading">
          切换中...
        </div>
      </template>
    </Suspense>
  </div>
</template>

4. 实际应用示例

4.1 数据表格组件

<!-- DataTable.vue -->
<script setup>
import { ref } from 'vue'

const props = defineProps({
  url: String,
  columns: Array
})

async function loadData() {
  const response = await fetch(props.url)
  const data = await response.json()
  return { data }
}

const { data } = await loadData()
</script>

<template>
  <table class="data-table">
    <thead>
      <tr>
        <th v-for="col in columns" :key="col.key">
          {{ col.title }}
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in data" :key="row.id">
        <td v-for="col in columns" :key="col.key">
          {{ row[col.key] }}
        </td>
      </tr>
    </tbody>
  </table>
</template>

<!-- 使用数据表格 -->
<template>
  <Suspense>
    <template #default>
      <data-table
        url="/api/users"
        :columns="[
          { key: 'name', title: '姓名' },
          { key: 'email', title: '邮箱' },
          { key: 'role', title: '角色' }
        ]"
      />
    </template>
    <template #fallback>
      <table-skeleton :columns="3" :rows="5" />
    </template>
  </Suspense>
</template>

4.2 图表组件

<!-- ChartComponent.vue -->
<script setup>
import { onMounted } from 'vue'
import * as echarts from 'echarts'

async function initChart() {
  // 模拟异步数据加载
  const data = await fetch('/api/chart-data').then(r => r.json())
  
  const chart = echarts.init(document.getElementById('chart'))
  chart.setOption({
    // 图表配置
    series: [
      {
        type: 'line',
        data: data
      }
    ]
  })
  
  return { chart }
}

const { chart } = await initChart()

onMounted(() => {
  window.addEventListener('resize', () => {
    chart.resize()
  })
})
</script>

<template>
  <div id="chart" style="width: 100%; height: 400px;"></div>
</template>

<!-- 使用图表组件 -->
<template>
  <div class="dashboard-chart">
    <Suspense>
      <template #default>
        <chart-component />
      </template>
      <template #fallback>
        <div class="chart-loading">
          <loading-spinner />
          <p>加载图表数据...</p>
        </div>
      </template>
    </Suspense>
  </div>
</template>

5. 最佳实践

5.1 组件设计

// 将异步逻辑抽离到组合式函数
export function useAsyncData(url) {
  return new Promise(async (resolve) => {
    const data = await fetch(url).then(r => r.json())
    resolve(data)
  })
}

// 在组件中使用
const data = await useAsyncData('/api/data')

5.2 加载状态管理

<!-- 提供更细粒度的加载状态 -->
<template>
  <Suspense>
    <template #default>
      <async-component />
    </template>
    <template #fallback>
      <div class="loading-state">
        <loading-spinner />
        <loading-progress :progress="loadingProgress" />
        <p>{{ loadingMessage }}</p>
      </div>
    </template>
  </Suspense>
</template>

6. 注意事项

  1. 避免无限循环
// ❌ 错误示例
async function setup() {
  const data = ref(null)
  data.value = await fetchData() // 可能导致无限循环
  return { data }
}

// ✅ 正确示例
const data = await fetchData()
  1. 合理的超时处理
<script setup>
import { ref, onMounted } from 'vue'

const timeout = ref(false)

onMounted(() => {
  setTimeout(() => {
    timeout.value = true
  }, 5000)
})
</script>

<template>
  <Suspense>
    <template #default>
      <async-component />
    </template>
    <template #fallback>
      <div>
        <loading-spinner />
        <p v-if="timeout">
          加载时间过长,请检查网络连接
        </p>
      </div>
    </template>
  </Suspense>
</template>
  1. 资源清理
<script setup>
import { onUnmounted } from 'vue'

const controller = new AbortController()
const data = await fetch('/api/data', {
  signal: controller.signal
})

onUnmounted(() => {
  controller.abort() // 取消未完成的请求
})
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/960250.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PyCharm接入DeepSeek实现AI编程

目录 效果演示 创建API key 在PyCharm中下载CodeGPT插件 配置Continue DeepSeek 是一家专注于人工智能技术研发的公司&#xff0c;致力于开发高性能、低成本的 AI 模型。DeepSeek-V3 是 DeepSeek 公司推出的最新一代 AI 模型。其前身是 DeepSeek-V2.5&#xff0c;经过持续的…

基于自然语言处理的垃圾短信识别系统

基于自然语言处理的垃圾短信识别系统 &#x1f31f; 嗨&#xff0c;我是LucianaiB&#xff01; &#x1f30d; 总有人间一两风&#xff0c;填我十万八千梦。 &#x1f680; 路漫漫其修远兮&#xff0c;吾将上下而求索。 目录 设计题目设计目的设计任务描述设计要求输入和输出…

类和对象(4)——多态:方法重写与动态绑定、向上转型和向下转型、多态的实现条件

目录 1. 向上转型和向下转型 1.1 向上转型 1.2 向下转型 1.3 instanceof关键字 2. 重写&#xff08;overidde&#xff09; 2.1 方法重写的规则 2.1.1 基础规则 2.1.2 深层规则 2.2 三种不能重写的方法 final修饰 private修饰 static修饰 3. 动态绑定 3.1 动态绑…

JavaScript使用toFixed保留一位小数的踩坑记录:TypeError: xxx.toFixed is not a function

JavaScript的toFixed函数是用于将一个数字格式化为指定的小数位数的字符串。其语法如下: numObj.toFixed([digits]) 其中,numObj是需要格式化的数字,digits是保留的小数位数。digits参数是一个可选参数,默认值为0,表示不保留小数位。 计算后需要保留一位小数,于是使用…

网络仿真工具Core环境搭建

目录 安装依赖包 源码下载 Core安装 FAQ 下载源码TLS出错误 问题 解决方案 找不到dbus-launch 问题 解决方案 安装依赖包 调用以下命令安装依赖包 apt-get install -y ca-certificates git sudo wget tzdata libpcap-dev libpcre3-dev \ libprotobuf-dev libxml2-de…

深入 Rollup:从入门到精通(三)Rollup CLI命令行实战

准备阶段&#xff1a;初始化项目 初始化项目&#xff0c;这里使用的是pnpm&#xff0c;也可以使用yarn或者npm # npm npm init -y # yarn yarn init -y # pnpm pnpm init安装rollup # npm npm install rollup -D # yarn yarn add rollup -D # pnpm pnpm install rollup -D在…

volatile之四类内存屏障指令 内存屏障 面试重点 底层源码

目录 volatile 两大特性 可见性 有序性 总结 什么是内存屏障 四个 CPU 指令 四大屏障 重排 重排的类型 为什么会有重排&#xff1f; 线程中的重排和可见性问题 如何防止重排引发的问题&#xff1f; 总结 happens-before 和 volatile 变量规则 内存屏障指令 写操作…

力扣算法题——11.盛最多水的容器

目录 &#x1f495;1.题目 &#x1f495;2.解析思路 本题思路总览 借助双指针探索规律 从规律到代码实现的转化 双指针的具体实现 代码整体流程 &#x1f495;3.代码实现 &#x1f495;4.完结 二十七步也能走完逆流河吗 &#x1f495;1.题目 &#x1f495;2.解析思路…

RK3568 adb使用

文章目录 一、adb介绍**ADB 主要功能****常用 ADB 命令****如何使用 ADB****总结** 二、Linux下载adb**方法 1&#xff1a;使用包管理器&#xff08;适用于 Ubuntu/Debian 系统&#xff09;****方法 2&#xff1a;通过 Snap 安装&#xff08;适用于支持 Snap 的系统&#xff09…

【ES实战】治理项之索引模板相关治理

索引模板治理 文章目录 索引模板治理问题现象分析思路操作步骤问题程序化方案索引与索引模板增加分片数校验管理 彩蛋如何查询Flink on Yarn 模式下的Task Manager日志相关配置查询已停止的Flink任务查询未停止的Flink任务 问题现象 在集群索引新建时&#xff0c;索引的分片比…

网络工程师 (2)计算机体系结构

一、冯诺依曼体系结构 &#xff08;一&#xff09;简介 冯诺依曼结构也称普林斯顿结构&#xff0c;是一种将程序指令存储器和数据存储器合并在一起的存储器结构。程序指令存储地址和数据存储地址指向同一个存储器的不同物理位置&#xff0c;因此程序指令和数据的宽度相同。数学…

Android Studio:视图绑定的岁月变迁(2/100)

一、博文导读 本文是基于Android Studio真实项目&#xff0c;通过解析源码了解真实应用场景&#xff0c;写文的视角和读者是同步的&#xff0c;想到看到写到&#xff0c;没有上帝视角。 前期回顾&#xff0c;本文是第二期。 private Unbinder mUnbinder; 只是声明了一个 接口…

LeetCode | 不同路径

一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&#xff1f; 示例 1…

低代码系统-产品架构案例介绍、得帆云(八)

产品名称 得帆云DeCode低代码平台-私有化 得帆云DeMDM主数据管理平台 得帆云DeCode低代码平台-公有云 得帆云DePortal企业门户 得帆云DeFusion融合集成平台 得帆云DeHoop数据中台 名词 概念 云原生 指自己搭建的运维平台&#xff0c;区别于阿里云、腾讯云 Dehoop 指…

使用ensp进行ppp协议综合实验

实验拓扑 实验划分 AR1的Serial3/0/0接口&#xff1a;192.168.1.1/24&#xff1b; AR2的Serial3/0/0接口&#xff1a;192.168.1.2/24&#xff1b; AR2的Serial3/0/1和4/0/0的聚合接口&#xff1a;192.168.2.2/24&#xff1b; AR3的Serial3/0/0和3/0/1的聚合接口&#xff1a;192…

【Python・机器学习】多元回归模型(原理及代码)

前言 自学笔记&#xff0c;分享给语言学/语言教育学方向的&#xff0c;但对语言数据处理感兴趣但是尚未入门&#xff0c;却需要在论文中用到的小伙伴&#xff0c;欢迎大佬们补充或绕道。ps&#xff1a;本文最少限度涉及公式讲解&#xff08;文科生小白友好体质&#xff09;&am…

unity免费资源2025-1-26

https://assetstore.unity.com/packages/tools/animation/motion-warping-climb-interact-270046 兑换码KINEMATION2025

Kitchen Racks 2

Kitchen Racks 2 吸盘置物架 Kitchen Racks-CSDN博客

ESMC-600M蛋白质语言模型本地部署攻略

前言 之前介绍了ESMC-6B模型的网络接口调用方法&#xff0c;但申请token比较慢&#xff0c;有网友问能不能出一个本地部署ESMC小模型的攻略&#xff0c;遂有本文。 其实本地部署并不复杂&#xff0c;官方github上面也比较清楚了。 操作过程 环境配置&#xff1a;CUDA 12.1、…

JAVA设计模式:依赖倒转原则(DIP)在Spring框架中的实践体现

文章目录 一、DIP原则深度解析1.1 核心定义1.2 现实比喻 二、Spring中的DIP实现机制2.1 传统实现 vs Spring实现对比 三、Spring中DIP的完整示例3.1 领域模型定义3.2 具体实现3.3 高层业务类3.4 配置类 四、Spring实现DIP的关键技术4.1 依赖注入方式对比4.2 自动装配注解 五、D…