在 Vue 开发中,除了我们日常编写的业务组件外,框架还提供了一系列内置组件,它们为我们处理常见的开发场景提供了优雅的解决方案。今天,我们就来深入探讨 Vue 的五大内置组件:
Transition、TransitionGroup、KeepAlive、Teleport和Suspense。
1. Transition - 丝滑的过渡动画
什么是 Transition?
Transition 组件用于在元素或组件的插入、更新和移除时添加动画效果,让用户体验更加流畅。
基本使用
1<template> 2 <button @click="show = !show">切换</button> 3 4 <Transition name="fade"> 5 <p v-if="show">Hello, Vue!</p> 6 </Transition> 7</template> 8 9<script setup> 10import { ref } from 'vue' 11 12const show = ref(true) 13</script> 14 15<style scoped> 16.fade-enter-active, 17.fade-leave-active { 18 transition: opacity 0.5s ease; 19} 20 21.fade-enter-from, 22.fade-leave-to { 23 opacity: 0; 24} 25</style> 26
过渡类名详解
Transition 组件会自动应用以下 6 个 CSS 类名:
v-enter-from:进入动画的起始状态v-enter-active:进入动画的激活状态v-enter-to:进入动画的结束状态v-leave-from:离开动画的起始状态v-leave-active:离开动画的激活状态v-leave-to:离开动画的结束状态
注意:其中的 v 是默认前缀,可以通过 name 属性自定义。
JavaScript 钩子
除了 CSS 过渡,还可以使用 JavaScript 钩子:
1<template> 2 <Transition 3 @before-enter="onBeforeEnter" 4 @enter="onEnter" 5 @after-enter="onAfterEnter" 6 @enter-cancelled="onEnterCancelled" 7 @before-leave="onBeforeLeave" 8 @leave="onLeave" 9 @after-leave="onAfterLeave" 10 @leave-cancelled="onLeaveCancelled" 11 > 12 <div v-if="show">内容</div> 13 </Transition> 14</template> 15 16<script setup> 17import { ref } from 'vue' 18 19const show = ref(true) 20 21const onBeforeEnter = (el) => { 22 // 元素插入 DOM 前的回调 23} 24 25const onEnter = (el, done) => { 26 // 元素插入 DOM 后的回调 27 // 需要手动调用 done() 来结束过渡 28 done() 29} 30</script> 31
模式控制
1<Transition mode="out-in"> 2 <button :key="isEditing" @click="isEditing = !isEditing"> 3 {{ isEditing ? '保存' : '编辑' }} 4 </button> 5</Transition> 6
支持的模式:
in-out:新元素先进入,当前元素后离开out-in:当前元素先离开,新元素后进入
2. TransitionGroup - 列表过渡专家
什么是 TransitionGroup?
TransitionGroup 组件专门用于处理动态列表中元素的进入、离开和移动的动画效果。
基本使用
1<template> 2 <button @click="addItem">添加</button> 3 <button @click="removeItem">移除</button> 4 5 <TransitionGroup name="list" tag="ul"> 6 <li v-for="item in items" :key="item.id"> 7 {{ item.text }} 8 </li> 9 </TransitionGroup> 10</template> 11 12<script setup> 13import { ref } from 'vue' 14 15const items = ref([ 16 { id: 1, text: '项目 1' }, 17 { id: 2, text: '项目 2' }, 18 { id: 3, text: '项目 3' } 19]) 20 21let nextId = 4 22 23const addItem = () => { 24 items.value.push({ id: nextId++, text: `项目 ${nextId}` }) 25} 26 27const removeItem = () => { 28 items.value.pop() 29} 30</script> 31 32<style scoped> 33.list-enter-active, 34.list-leave-active { 35 transition: all 0.5s ease; 36} 37.list-enter-from { 38 opacity: 0; 39 transform: translateX(30px); 40} 41.list-leave-to { 42 opacity: 0; 43 transform: translateX(-30px); 44} 45/* 确保离开的元素脱离文档流 */ 46.list-leave-active { 47 position: absolute; 48} 49</style> 50
关键特性
- 必须设置 key:每个元素都需要唯一的 key
- 支持 CSS 变换:自动检测元素位置变化应用移动动画
- tag 属性:指定包裹容器的标签,默认为不渲染包裹元素
3. KeepAlive - 组件缓存大师
什么是 KeepAlive?
KeepAlive 组件用于缓存不活动的组件实例,避免重复渲染,保持组件状态。
基本使用
1<template> 2 <div> 3 <button @click="currentTab = 'Home'">首页</button> 4 <button @click="currentTab = 'About'">关于</button> 5 6 <KeepAlive> 7 <component :is="currentTab" /> 8 </KeepAlive> 9 </div> 10</template> 11 12<script setup> 13import { ref, shallowRef } from 'vue' 14import Home from './Home.vue' 15import About from './About.vue' 16 17const currentTab = ref('Home') 18const tabs = { 19 Home, 20 About 21} 22</script> 23
高级配置
1<KeepAlive 2 :include="/Home|About/" 3 :exclude="['Settings']" 4 :max="10" 5> 6 <component :is="currentComponent" /> 7</KeepAlive> 8
- include:只有名称匹配的组件会被缓存(字符串、正则或数组)
- exclude:任何名称匹配的组件都不会被缓存
- max:最多可缓存的组件实例数量
生命周期钩子
被缓存的组件会获得两个新的生命周期钩子:
1<script setup> 2import { onActivated, onDeactivated } from 'vue' 3 4onActivated(() => { 5 // 组件被激活时调用 6 console.log('组件激活') 7}) 8 9onDeactivated(() => { 10 // 组件被停用时调用 11 console.log('组件停用') 12}) 13</script> 14
4. Teleport - 任意门组件
什么是 Teleport?
Teleport 组件允许我们将组件模板的一部分"传送"到 DOM 中的其他位置,而不影响组件的逻辑关系。
基本使用
1<template> 2 <div class="app"> 3 <button @click="showModal = true">打开模态框</button> 4 5 <Teleport to="body"> 6 <div v-if="showModal" class="modal"> 7 <div class="modal-content"> 8 <h2>模态框标题</h2> 9 <p>这是模态框内容</p> 10 <button @click="showModal = false">关闭</button> 11 </div> 12 </div> 13 </Teleport> 14 </div> 15</template> 16 17<script setup> 18import { ref } from 'vue' 19 20const showModal = ref(false) 21</script> 22 23<style scoped> 24.modal { 25 position: fixed; 26 top: 0; 27 left: 0; 28 right: 0; 29 bottom: 0; 30 background: rgba(0, 0, 0, 0.5); 31 display: flex; 32 justify-content: center; 33 align-items: center; 34} 35 36.modal-content { 37 background: white; 38 padding: 20px; 39 border-radius: 8px; 40} 41</style> 42
多个 Teleport 到同一目标
1<template> 2 <Teleport to="#modals"> 3 <div>第一个模态框</div> 4 </Teleport> 5 6 <Teleport to="#modals"> 7 <div>第二个模态框</div> 8 </Teleport> 9</template> 10
渲染结果:
1<div id="modals"> 2 <div>第一个模态框</div> 3 <div>第二个模态框</div> 4</div> 5
禁用 Teleport
1<Teleport to="body" :disabled="isMobile"> 2 <div>内容</div> 3</Teleport> 4
5. Suspense - 异步组件管家
什么是 Suspense?
Suspense 组件用于协调组件树中的异步依赖,在等待异步组件时显示加载状态。
基本使用
1<template> 2 <Suspense> 3 <template #default> 4 <AsyncComponent /> 5 </template> 6 7 <template #fallback> 8 <div class="loading">加载中...</div> 9 </template> 10 </Suspense> 11</template> 12 13<script setup> 14import { defineAsyncComponent } from 'vue' 15 16const AsyncComponent = defineAsyncComponent(() => 17 import('./AsyncComponent.vue') 18) 19</script> 20 21<style scoped> 22.loading { 23 text-align: center; 24 padding: 20px; 25 color: #666; 26} 27</style> 28
异步 setup 组件
1<template> 2 <Suspense> 3 <template #default> 4 <ComponentWithAsyncSetup /> 5 </template> 6 7 <template #fallback> 8 <div>加载用户数据...</div> 9 </template> 10 </Suspense> 11</template> 12 13<script setup> 14import { ref } from 'vue' 15 16const ComponentWithAsyncSetup = { 17 async setup() { 18 const userData = await fetchUserData() 19 return { userData } 20 }, 21 template: [`<div>用户: {{ userData.name }}</div>`](https://xplanc.org/primers/document/zh/03.HTML/EX.HTML%20%E5%85%83%E7%B4%A0/EX.div.md) 22} 23 24async function fetchUserData() { 25 // 模拟 API 调用 26 return new Promise(resolve => { 27 setTimeout(() => { 28 resolve({ name: 'Vue 开发者' }) 29 }, 2000) 30 }) 31} 32</script> 33
事件处理
1<template> 2 <Suspense @pending="onPending" @resolve="onResolve" @fallback="onFallback"> 3 <AsyncComponent /> 4 </Suspense> 5</template> 6 7<script setup> 8const onPending = () => { 9 console.log('开始加载异步组件') 10} 11 12const onResolve = () => { 13 console.log('异步组件加载完成') 14} 15 16const onFallback = () => { 17 console.log('显示加载状态') 18} 19</script> 20
实战案例:组合使用内置组件
让我们看一个综合使用多个内置组件的例子:
1<template> 2 <div class="app"> 3 <!-- 标签页切换 --> 4 <nav> 5 <button 6 v-for="tab in tabs" 7 :key="tab.id" 8 @click="currentTab = tab.id" 9 > 10 {{ tab.name }} 11 </button> 12 </nav> 13 14 <!-- 主要内容区域 --> 15 <main> 16 <KeepAlive> 17 <Transition name="slide" mode="out-in"> 18 <Suspense> 19 <template #default> 20 <component :is="currentTabComponent" /> 21 </template> 22 <template #fallback> 23 <div class="loading">加载中...</div> 24 </template> 25 </Suspense> 26 </Transition> 27 </KeepAlive> 28 </main> 29 30 <!-- 全局通知 --> 31 <Teleport to="#notifications"> 32 <TransitionGroup name="notification"> 33 <div 34 v-for="notification in notifications" 35 :key="notification.id" 36 class="notification" 37 > 38 {{ notification.message }} 39 </div> 40 </TransitionGroup> 41 </Teleport> 42 </div> 43</template> 44 45<script setup> 46import { ref, computed, defineAsyncComponent } from 'vue' 47 48// 标签页状态 49const currentTab = ref('home') 50const tabs = [ 51 { id: 'home', name: '首页' }, 52 { id: 'profile', name: '个人资料' }, 53 { id: 'settings', name: '设置' } 54] 55 56// 异步组件 57const currentTabComponent = computed(() => 58 defineAsyncComponent(() => import(`./${currentTab.value}.vue`)) 59) 60 61// 通知系统 62const notifications = ref([]) 63 64// 添加通知 65const addNotification = (message) => { 66 const id = Date.now() 67 notifications.value.push({ id, message }) 68 setTimeout(() => { 69 notifications.value = notifications.value.filter(n => n.id !== id) 70 }, 3000) 71} 72</script> 73 74<style scoped> 75.slide-enter-active, 76.slide-leave-active { 77 transition: all 0.3s ease; 78} 79 80.slide-enter-from { 81 opacity: 0; 82 transform: translateX(50px); 83} 84 85.slide-leave-to { 86 opacity: 0; 87 transform: translateX(-50px); 88} 89 90.notification-enter-active, 91.notification-leave-active { 92 transition: all 0.3s ease; 93} 94 95.notification-enter-from { 96 opacity: 0; 97 transform: translateY(-30px); 98} 99 100.notification-leave-to { 101 opacity: 0; 102 transform: translateX(100%); 103} 104 105.loading { 106 text-align: center; 107 padding: 50px; 108 font-size: 18px; 109} 110</style> 111
总结
Vue 的内置组件为我们提供了强大的工具来处理常见的开发需求:
- Transition:为单个元素/组件添加过渡动画
- TransitionGroup:为列表项添加排序和动画效果
- KeepAlive:缓存组件状态,提升性能
- Teleport:将内容渲染到 DOM 任意位置
- Suspense:优雅处理异步组件加载状态
这些内置组件不仅功能强大,而且可以灵活组合使用,帮助我们构建更加优雅、高效的 Vue 应用。掌握它们的使用,将让你的 Vue 开发技能更上一层楼!
希望这篇文章能帮助你全面理解 Vue 的内置组件,并在实际项目中灵活运用。Happy Coding! 🚀
《Vue 内置组件全解析:提升开发效率的五大神器》 是转载文章,点击查看原文。
