Appearance
【Vue】异步组件 👌
要点速览
- 定义:按需加载的组件,通过延迟加载降低首屏体积与初始化开销。
- 用法:
defineAsyncComponent(() => import('./MyComp.vue'))返回一个可直接使用的组件。 - 体验:可配置
loadingComponent/errorComponent/delay/timeout优化加载体验。 - 组织:支持全局注册或在父组件中内联定义;常与
Suspense搭配。 - 场景:路由页面、体积较大或低频使用的组件、后台管理模块等。
动机与定义
在应用启动阶段同步导入大量组件会增加首屏体积与加载时间。异步组件通过在“被需要时”才加载的方式,将代码拆分为独立块并按需获取,从而改善性能与交互体验。
基本用法
js
import { defineAsyncComponent } from "vue";
const AsyncCom = defineAsyncComponent(() => import("./MyCom.vue"));也可使用工厂函数手动返回一个 Promise:
js
import { defineAsyncComponent } from "vue";
const AsyncCom = defineAsyncComponent(
() =>
new Promise((resolve) => {
resolve(/* 组件模块 */);
})
);快速上手
src/
├── components/
│ ├── Home.vue
│ └── About.vue
├── App.vue
└── main.js同步导入的写法会在应用启动时加载所有页面组件:
vue
<template>
<div id="app">
<button @click="currentComponent = Home">访问主页</button>
<button @click="currentComponent = About">访问关于</button>
<component :is="currentComponent" v-if="currentComponent" />
</div>
</template>
<script setup>
import { shallowRef } from "vue";
import Home from "./components/Home.vue";
import About from "./components/About.vue";
const currentComponent = shallowRef(null);
</script>改为按需加载:
vue
<template>
<div id="app">
<button @click="loadComponent('Home')">访问主页</button>
<button @click="loadComponent('About')">访问关于</button>
<component :is="currentComponent" v-if="currentComponent" />
</div>
</template>
<script setup>
import { shallowRef, defineAsyncComponent } from "vue";
const currentComponent = shallowRef(null);
const loadComponent = (name) => {
currentComponent.value = defineAsyncComponent(() =>
import(`./components/${name}.vue`)
);
};
</script>defineAsyncComponents默认会缓存加载过的模块,避免重复加载。这意味着如果用户多次访问同一个组件,它只会被加载一次。
现在组件只有在用户点击时才加载,实现了懒加载与代码分割。
相关细节
全局注册
js
import { createApp, defineAsyncComponent } from "vue";
const app = createApp(/* ... */);
app.component(
"MyComponent",
defineAsyncComponent(() => import("./components/MyComponent.vue"))
);在父组件中定义
vue
<script setup>
import { defineAsyncComponent } from "vue";
const AdminPage = defineAsyncComponent(() =>
import("./components/AdminPageComponent.vue")
);
</script>
<template>
<AdminPage />
</template>配置项
js
import { defineAsyncComponent } from "vue";
const AsyncComp = defineAsyncComponent({
loader: () => import("./Foo.vue"),
loadingComponent: LoadingComponent,
delay: 200,
errorComponent: ErrorComponent,
timeout: 3000,
});配置项说明:
loader:返回组件模块的异步函数;通常为() => import('...')。loadingComponent:加载过程中展示的占位组件,配合delay避免闪烁。delay:展示loadingComponent的延迟(毫秒);网络快时不显示占位以减少抖动。errorComponent:加载失败时展示的组件;与timeout/onError配合更友好。timeout:超时阈值(毫秒);超过后视为失败,渲染errorComponent。suspensible:是否参与Suspense(默认true);设为false时不阻塞渲染,直接显示 loading。onError(error, retry, fail, attempts):加载失败的重试钩子;可自定义重试策略。
高级用法(重试与取消):
js
import { defineAsyncComponent } from "vue";
const AsyncComp = defineAsyncComponent({
loader: () => import("./Foo.vue"),
errorComponent: ErrorComponent,
delay: 200,
timeout: 5000,
suspensible: true,
onError(error, retry, fail, attempts) {
const max = 3;
if (attempts <= max) {
const wait = 1000 * attempts;
setTimeout(() => retry(), wait);
} else {
fail();
}
},
});常见误区与实践建议
- 忘记返回 Promise:loader 必须返回
import()或其他 Promise。 - 过度切分:体积极小的组件不必异步化,避免产生过多请求。
loadingComponent闪烁:通过delay避免在快速网络下闪烁。- 错误处理:提供
errorComponent与合理的timeout,并在控制台监控加载错误。
用户体验与性能
- 与路由按需加载结合:为页面组件做代码分割,减小首屏包体积。
- 预取与预加载:在用户可能访问前触发预取,缩短感知等待时间。
- 搭配
Suspense:在父层使用占位与回退内容,统一管理加载中的界面表现。
异步组件经常与内置组件 Suspense 搭配使用,以提供更平滑的用户体验。
