当前位置: 首页 > article >正文

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>

http://www.kler.cn/a/520168.html

相关文章:

  • Synology 群辉NAS安装(6)安装mssql
  • decison tree 决策树
  • 分公司-国内外大公司-github等开源项目推荐笔记
  • 【Postgres_Python】使用python脚本批量创建和导入多个PG数据库
  • 巴菲特购买比特币
  • SpringBoot+Electron教务管理系统 附带详细运行指导视频
  • 视频多模态模型——视频版ViT
  • C++小病毒-1.0勒索(更新次数:2)
  • WPF实战案例 | C# WPF实现大学选课系统
  • salesforce 可以 outbound profile 吗
  • Mac 上如何同时运行多个MySQL版本?
  • 基于微信小程序的停车场管理系统设计 停车场微信小程序的设计与实现 (源码+文档)
  • 2025年危化品经营单位生产管理人员考试真题附答案
  • 【Elasticsearch】doc_values 可以用于查询操作
  • Pyecharts之散点图的视觉扩展
  • C语言初阶力扣刷题——349. 两个数组的交集【难度:简单】
  • AJAX RSS Reader:技术解析与应用场景
  • ESMC-600M蛋白质语言模型本地部署攻略
  • 【C++高并发服务器WebServer】-2:exec函数簇、进程控制
  • 【2024年华为OD机试】(A卷,100分)- 货币单位换算 (JavaScriptJava PythonC/C++)