动态组件与异步组件
Vue 3 提供了动态组件和异步组件两种机制,用于在运行时灵活加载和切换组件。动态组件通过 <component> 标签实现组件的动态渲染,而异步组件则通过懒加载优化性能,特别适合大型应用。本节将详细讲解这两者的用法、实现方式和应用场景,帮助你掌握这些高级组件化技术。
动态组件
动态组件允许在运行时根据条件切换渲染的组件,使用 <component> 标签和 :is 属性实现。
基本用法
示例:切换组件
<template>
<div>
<button @click="currentComponent = 'CompA'">显示 A</button>
<button @click="currentComponent = 'CompB'">显示 B</button>
<component :is="currentComponent" />
</div>
</template>
<script>
import { ref } from 'vue';
import CompA from './CompA.vue';
import CompB from './CompB.vue';
export default {
components: { CompA, CompB },
setup() {
const currentComponent = ref('CompA');
return { currentComponent };
}
};
</script>
子组件
<!-- CompA.vue -->
<template>
<p>组件 A</p>
</template>
<!-- CompB.vue -->
<template>
<p>组件 B</p>
</template>
- 效果:点击按钮切换显示“组件 A”或“组件 B”。
- 解析:
:is绑定组件名称或组件对象。- 支持局部注册的组件或动态导入。
传递 Props 和事件
动态组件可以接收 Props 和监听事件:
<template>
<div>
<button @click="currentComponent = 'CompA'">显示 A</button>
<button @click="currentComponent = 'CompB'">显示 B</button>
<component :is="currentComponent" :message="message" @custom-event="handleEvent" />
</div>
</template>
<script>
import { ref } from 'vue';
import CompA from './CompA.vue';
import CompB from './CompB.vue';
export default {
components: { CompA, CompB },
setup() {
const currentComponent = ref('CompA');
const message = ref('Hello from parent');
const handleEvent = (value) => console.log('事件:', value);
return { currentComponent, message, handleEvent };
}
};
</script>
子组件(CompA.vue)
<template>
<p>{{ message }}</p>
<button @click="$emit('custom-event', 'From A')">触发事件</button>
</template>
<script>
export default {
props: ['message'],
emits: ['custom-event']
};
</script>
- 效果:切换组件时传递消息,点击按钮触发事件。
keep-alive 缓存
使用 <keep-alive> 包裹动态组件,缓存实例状态:
<template>
<div>
<button @click="currentComponent = 'CompA'">显示 A</button>
<button @click="currentComponent = 'CompB'">显示 B</button>
<keep-alive>
<component :is="currentComponent" />
</keep-alive>
</div>
</template>
<script>
import { ref } from 'vue';
import CompA from './CompA.vue';
import CompB from './CompB.vue';
export default {
components: { CompA, CompB },
setup() {
const currentComponent = ref('CompA');
return { currentComponent };
}
};
</script>
子组件(CompA.vue)
<template>
<div>
<p>组件 A</p>
<input v-model="input" />
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const input = ref('');
return { input };
}
};
</script>
- 效果:切换组件后,输入框内容保留。
- 解析:
<keep-alive>缓存组件实例,避免销毁和重建。
异步组件
异步组件通过懒加载减少初始加载时间,适合按需加载大型或不常用的组件。
基本用法
使用 defineAsyncComponent 定义异步组件:
<template>
<div>
<button @click="showComponent = !showComponent">切换显示</button>
<AsyncComp v-if="showComponent" />
</div>
</template>
<script>
import { ref, defineAsyncComponent } from 'vue';
export default {
components: {
AsyncComp: defineAsyncComponent(() => import('./AsyncComp.vue'))
},
setup() {
const showComponent = ref(false);
return { showComponent };
}
};
</script>
子组件(AsyncComp.vue)
<template>
<p>异步加载的组件</p>
</template>
- 效果:点击按钮时才加载
AsyncComp.vue。 - 解析:
import()返回 Promise,组件按需加载。
加载状态与错误处理
支持加载中和错误时的自定义显示:
<template>
<div>
<button @click="showComponent = !showComponent">切换</button>
<AsyncComp v-if="showComponent" />
</div>
</template>
<script>
import { ref, defineAsyncComponent } from 'vue';
export default {
components: {
AsyncComp: defineAsyncComponent({
loader: () => import('./AsyncComp.vue'),
loadingComponent: { template: '<p>加载中...</p>' },
errorComponent: { template: '<p>加载失败</p>' },
delay: 200, // 加载前延迟(ms)
timeout: 3000 // 超时时间(ms)
})
},
setup() {
const showComponent = ref(false);
return { showComponent };
}
};
</script>
- 效果:
- 加载前显示“加载中...”,延迟 200ms。
- 超时 3s 显示“加载失败”。
与 Suspense 结合
Vue 3 的 <Suspense> 组件可统一管理异步组件的加载状态:
<template>
<div>
<button @click="showComponent = !showComponent">切换</button>
<Suspense v-if="showComponent">
<template #default>
<AsyncComp />
</template>
<template #fallback>
<p>加载中...</p>
</template>
</Suspense>
</div>
</template>
<script>
import { ref, defineAsyncComponent } from 'vue';
export default {
components: {
AsyncComp: defineAsyncComponent(() => import('./AsyncComp.vue'))
},
setup() {
const showComponent = ref(false);
return { showComponent };
}
};
</script>
- 效果:加载期间显示“加载中...”,完成后渲染组件。
- 注意:
<Suspense>是实验性特性,需关注其稳定性。
应用场景
动态组件
- 选项卡:根据用户选择切换内容。
- 模态框:动态加载不同弹窗内容。
- 权限控制:根据角色渲染不同组件。
示例:选项卡
<template>
<div>
<button v-for="tab in tabs" :key="tab" @click="currentTab = tab">
{{ tab }}
</button>
<keep-alive>
<component :is="currentTab" />
</keep-alive>
</div>
</template>
<script>
import { ref } from 'vue';
import TabA from './TabA.vue';
import TabB from './TabB.vue';
export default {
components: { TabA, TabB },
setup() {
const tabs = ['TabA', 'TabB'];
const currentTab = ref('TabA');
return { tabs, currentTab };
}
};
</script>
异步组件
- 路由懒加载:按需加载页面组件。
- 大型模块:如图表库、编辑器。
- 条件加载:仅在特定条件下加载。
示例:懒加载图表
<template>
<div>
<button @click="showChart = true">显示图表</button>
<ChartComp v-if="showChart" />
</div>
</template>
<script>
import { ref, defineAsyncComponent } from 'vue';
export default {
components: {
ChartComp: defineAsyncComponent(() => import('./ChartComp.vue'))
},
setup() {
const showChart = ref(false);
return { showChart };
}
};
</script>
注意事项
- 动态组件命名:
- 使用注册的组件名或导入的对象,避免字符串拼写错误。
- 异步组件性能:
- 避免过多懒加载影响用户体验。
- 错误处理:
- 配置
errorComponent和timeout提升健壮性。
- 配置
总结
动态组件和异步组件是 Vue 3 组件化开发的利器。动态组件通过 <component> 实现运行时切换,搭配 <keep-alive> 优化体验;异步组件通过 defineAsyncComponent 和 <Suspense> 实现懒加载,减少初始加载负担。掌握这些技术后,你可以灵活应对复杂场景。下一节将探讨插槽的使用,进一步丰富组件功能!
