Suspense 异步组件加载
Suspense 是 Vue 3 引入的一项实验性特性,用于优雅地处理异步组件加载。它通过声明式方式管理加载状态和错误,提供统一的占位内容展示,特别适合懒加载场景。本节将详细讲解 Suspense 的工作原理、使用方法和实践场景,并通过示例展示如何在 Vue 3 项目中应用这一功能。
什么是 Suspense?
Suspense 是一个内置组件,能够捕获其子组件的异步依赖(如动态导入或 Promise),并在加载完成前显示占位内容。它解决了传统异步加载中手动管理状态的繁琐问题。
基本语法
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<p>加载中...</p>
</template>
</Suspense>
</template>
- 插槽:
#default:异步加载完成后的内容。#fallback:加载期间显示的占位内容。
工作原理
- 当子组件包含异步操作(如
import()或setup()返回的 Promise),Suspense会等待这些操作完成。 - 在等待期间,渲染
#fallback内容。
使用场景
Suspense 适用于以下场景:
- 懒加载组件:
- 按需加载大型组件或页面。
- 异步数据加载:
- 在组件挂载前获取数据。
- 路由集成:
- 与 Vue Router 的懒加载路由结合。
- 多组件协调:
- 管理多个异步子组件的加载状态。
基本用法
示例:懒加载组件
<template>
<div>
<button @click="show = !show">切换显示</button>
<Suspense v-if="show">
<template #default>
<AsyncComp />
</template>
<template #fallback>
<p>加载中...</p>
</template>
</Suspense>
</div>
</template>
<script>
import { ref, defineAsyncComponent } from 'vue';
export default {
components: {
AsyncComp: defineAsyncComponent(() =>
new Promise(resolve => {
setTimeout(() => resolve(import('./AsyncComp.vue')), 1000);
})
)
},
setup() {
const show = ref(false);
return { show };
}
};
</script>
子组件
<!-- AsyncComp.vue -->
<template>
<p>异步组件已加载</p>
</template>
- 效果:点击按钮后显示“加载中...”,1 秒后切换为“异步组件已加载”。
示例:异步数据加载
<template>
<Suspense>
<template #default>
<DataComp />
</template>
<template #fallback>
<p>数据加载中...</p>
</template>
</Suspense>
</template>
<script>
import { defineAsyncComponent } from 'vue';
export default {
components: {
DataComp: defineAsyncComponent({
loader: () => import('./DataComp.vue'),
delay: 200
})
}
};
</script>
子组件
<!-- DataComp.vue -->
<template>
<p>数据: {{ data }}</p>
</template>
<script>
import { ref } from 'vue';
export default {
async setup() {
const data = ref(null);
const response = await new Promise(resolve => {
setTimeout(() => resolve('加载完成'), 1000);
});
data.value = response;
return { data };
}
};
</script>
- 效果:显示“数据加载中...”,1 秒后显示“数据: 加载完成”。
高级用法
1. 错误处理
Suspense 可与 v-if 或外部状态结合处理错误:
<template>
<div>
<Suspense v-if="!error">
<template #default>
<AsyncComp />
</template>
<template #fallback>
<p>加载中...</p>
</template>
</Suspense>
<p v-else>加载失败: {{ error }}</p>
<button @click="reload">重试</button>
</div>
</template>
<script>
import { ref, defineAsyncComponent } from 'vue';
export default {
components: {
AsyncComp: defineAsyncComponent({
loader: () => new Promise((_, reject) => {
setTimeout(() => reject('加载错误'), 1000);
}),
errorComponent: { template: '<div>错误</div>' },
timeout: 2000
})
},
setup() {
const error = ref(null);
const reload = () => {
error.value = null;
};
const loadComponent = async () => {
try {
await import('./AsyncComp.vue');
} catch (err) {
error.value = err.message;
}
};
loadComponent();
return { error, reload };
}
};
</script>
- 效果:加载失败后显示错误信息,点击重试重新加载。
2. 与路由结合
配合 Vue Router 的懒加载:
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{ path: '/', component: () => import('@/views/Home.vue') },
{ path: '/about', component: () => import('@/views/About.vue') }
];
const router = createRouter({
history: createWebHistory(),
routes
});
export default router;
<!-- App.vue -->
<template>
<div>
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<Suspense>
<template #default>
<router-view />
</template>
<template #fallback>
<p>页面加载中...</p>
</template>
</Suspense>
</div>
</template>
- 效果:导航时显示“页面加载中...”,加载完成切换页面。
应用场景
- 懒加载页面:
- 大型路由组件按需加载。
- 数据预加载:
- 组件渲染前获取 API 数据。
- 复杂组件:
- 处理多个异步依赖(如图表库)。
- 用户体验:
- 统一加载提示,提升一致性。
注意事项
- 实验性特性:
Suspense在 Vue 3.2 中仍为实验性,需关注版本稳定性。
- 异步依赖:
- 仅捕获
defineAsyncComponent或setup返回的 Promise。
- 仅捕获
- 嵌套限制:
- 不支持深层嵌套的
Suspense,需在顶层使用。
- 不支持深层嵌套的
- 错误边界:
- 需手动实现错误处理,
Suspense不提供内置方案。
- 需手动实现错误处理,
综合示例
动态加载用户列表
<template>
<div>
<Suspense>
<template #default>
<UserList />
</template>
<template #fallback>
<p>加载用户列表...</p>
</template>
</Suspense>
</div>
</template>
<script>
import { defineAsyncComponent } from 'vue';
export default {
components: {
UserList: defineAsyncComponent({
loader: () => import('./UserList.vue'),
delay: 200,
timeout: 3000
})
}
};
</script>
子组件
<!-- UserList.vue -->
<template>
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
</template>
<script>
import { ref } from 'vue';
export default {
async setup() {
const users = ref([]);
const response = await fetch('https://jsonplaceholder.typicode.com/users?_limit=3');
users.value = await response.json();
return { users };
}
};
</script>
- 效果:显示“加载用户列表...”,加载完成后显示用户列表。
- 解析:结合懒加载和异步数据请求,
Suspense统一管理状态。
总结
Suspense 为 Vue 3 的异步组件加载提供了声明式解决方案,通过 #fallback 和 #default 插槽简化了加载状态管理。虽然仍为实验性特性,但其在懒加载和数据预加载中的表现令人期待。掌握本节内容后,你可以更优雅地处理异步逻辑。下一节将探讨自定义渲染器,带你进入更深层次的 Vue 3 探索!
