Vue3 – Transition & 動畫效果:enter / leave 類別的完整指南
簡介
在單頁應用程式(SPA)中,視圖的切換往往伴隨著元素的顯示與隱藏。若沒有適當的過渡效果,使用者會感受到突兀的畫面變化,降低使用體驗。Vue 3 內建的 <transition> 元件提供了 enter / leave 類別(class)機制,讓開發者只需要撰寫 CSS,就能為任何元素或組件加入平滑的動畫。
本篇文章將深入說明 enter / leave 類別的運作原理、常見使用情境與最佳實踐,並配合多個實作範例,幫助從初學者到中階開發者快速上手,將動畫自然地融合到 Vue 3 專案中。
核心概念
1. <transition> 基礎
Vue 3 的 <transition> 只會影響它的 單一根元素(或單一根組件),在元素進入(enter)或離開(leave)時自動添加、移除特定的 CSS 類別。最常見的類別名稱如下(以 fade 為例):
| 階段 | 類別名稱 | 目的 |
|---|---|---|
| 進入開始 | fade-enter-from |
初始狀態(隱藏) |
| 進入中 | fade-enter-active |
動畫持續期間(設定 transition) |
| 進入結束 | fade-enter-to |
結束狀態(顯示) |
| 離開開始 | fade-leave-from |
初始狀態(顯示) |
| 離開中 | fade-leave-active |
動畫持續期間 |
| 離開結束 | fade-leave-to |
結束狀態(隱藏) |
重點:
-from、-to類別只會在同一幀(frame)內同時存在,-active類別則會在動畫全程持有。
2. 何時觸發?
- Enter:當綁定的條件(如
v-if、v-show)變為 true,或組件首次渲染且使用appear屬性時。 - Leave:當條件變為 false,或組件被銷毀前。
3. 自訂類別前綴
預設前綴是 v-(如 v-enter-active),但可以透過 name 屬性自行定義:
<transition name="slide">
<div v-if="show">滑入滑出</div>
</transition>
此時 Vue 會產生 slide-enter-*、slide-leave-* 系列類別。
4. mode 屬性
如果同時出現 enter 與 leave(例如列表切換),預設兩者會同時執行。mode="out-in" 或 mode="in-out" 可控制先後順序,避免動畫衝突。
程式碼範例
以下提供 五個 常見且實用的範例,從最簡單的淡入淡出到列表動態切換,並附上完整註解。
範例 1️⃣ 基本淡入淡出(Fade)
<!-- App.vue -->
<template>
<button @click="show = !show">Toggle</button>
<transition name="fade">
<div v-if="show" class="box">我會淡入淡出</div>
</transition>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(false)
</script>
<style scoped>
/* 1. 初始狀態:隱藏 */
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
/* 2. 結束狀態:顯示 */
.fade-enter-to,
.fade-leave-from {
opacity: 1;
}
/* 3. 動畫持續時間與曲線 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.4s ease;
}
</style>
說明:
v-if觸發enter、leave,fade-enter-from與fade-leave-to同時設定opacity: 0,讓元素在兩個階段都從透明開始或結束。
範例 2️⃣ 滑動進出(Slide)
<template>
<button @click="show = !show">Toggle Slide</button>
<transition name="slide" mode="out-in">
<div v-if="show" class="panel">滑動面板</div>
</transition>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(false)
</script>
<style scoped>
.slide-enter-from,
.slide-leave-to {
transform: translateX(-100%);
}
.slide-enter-to,
.slide-leave-from {
transform: translateX(0);
}
.slide-enter-active,
.slide-leave-active {
transition: transform 0.35s cubic-bezier(0.25, 0.8, 0.25, 1);
}
</style>
技巧:
mode="out-in"確保舊元素先滑出,再讓新元素滑入,避免同時出現兩個面板的視覺衝突。
範例 3️⃣ 列表項目過渡(List Transition)
<template>
<button @click="addItem">新增項目</button>
<button @click="removeItem">移除最後一項</button>
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item" class="list-item">
{{ item }}
</li>
</transition-group>
</template>
<script setup>
import { ref } from 'vue'
const items = ref([1, 2, 3])
function addItem() {
items.value.push(items.value.length + 1)
}
function removeItem() {
items.value.pop()
}
</script>
<style scoped>
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateY(20px);
}
.list-enter-to,
.list-leave-from {
opacity: 1;
transform: translateY(0);
}
.list-enter-active,
.list-leave-active {
transition: all 0.3s ease;
}
</style>
重點:使用
<transition-group>時,必須提供key,讓 Vue 能正確追蹤每個項目的進出。
範例 4️⃣ appear 屬性:首次渲染動畫
<template>
<transition name="fade" appear>
<div class="hero">首次載入也會淡入</div>
</transition>
</template>
<style scoped>
.fade-enter-from,
.fade-appear-from {
opacity: 0;
}
.fade-enter-to,
.fade-appear-to {
opacity: 1;
}
.fade-enter-active,
.fade-appear-active {
transition: opacity 0.6s ease-out;
}
</style>
說明:
appear讓組件在 掛載時 也會走一次enter流程,常用於首頁大圖或首次顯示的 UI 元件。
範例 5️⃣ 結合 JavaScript Hook:自訂動畫時長
<template>
<button @click="show = !show">Toggle (JS Hook)</button>
<transition
@enter="enter"
@leave="leave"
:css="false"
>
<div v-if="show" class="box">JS 控制動畫</div>
</transition>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(false)
function enter(el, done) {
el.style.opacity = 0
// 使用 requestAnimationFrame 確保樣式渲染
requestAnimationFrame(() => {
el.style.transition = 'opacity 0.5s ease'
el.style.opacity = 1
el.addEventListener('transitionend', done)
})
}
function leave(el, done) {
el.style.opacity = 1
requestAnimationFrame(() => {
el.style.transition = 'opacity 0.3s ease'
el.style.opacity = 0
el.addEventListener('transitionend', done)
})
}
</script>
<style scoped>
.box {
width: 200px;
height: 100px;
background: #42b983;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
</style>
應用:當 CSS 無法滿足需求(如根據資料動態決定時長)時,可關閉
css模式,改用enter/leaveJavaScript hook 完全自訂動畫。
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
| 類別名稱拼寫錯誤 | -enter-from、-enter-to 拼寫不一致會導致動畫失效。 |
使用 IDE 自動補全或在 name 後寫註解提醒。 |
忘記設定 transition 時間 |
只寫 -from / -to 而未加 -active,瀏覽器不會平滑過渡。 |
必須在 -active 中明確設定 transition 或 animation。 |
v-if 與 v-show 混用 |
v-show 只改變 display,不觸發 enter / leave。 |
想要動畫時,使用 v-if 或 appear。 |
列表過渡缺少 key |
Vue 無法辨識哪個項目是新增或刪除,導致不正確的動畫。 | 為每個 v-for 項目提供唯一且穩定的 :key。 |
mode 使用不當 |
同時進出動畫時,未設定 mode 可能產生閃爍或重疊。 |
根據 UI 需求選擇 out-in、in-out 或保留預設。 |
過度使用 transition-group |
大量項目同時動畫會造成效能下降。 | 使用 requestAnimationFrame 或限制同時動畫數量。 |
| CSS 優先度衝突 | 全局樣式覆寫了 scoped 動畫類別。 | 使用 scoped、::v-deep 或提升特異性 (!important 應慎用)。 |
最佳實踐:
- 保持類別語意化:使用
fade-、slide-等前綴,讓樣式易於維護。 - 將動畫抽離為 SCSS/LESS 變數:如
$transition-duration: 0.3s;,統一管理。 - 使用
prefers-reduced-motion:尊重使用者系統設定,提供無動畫備案。
@media (prefers-reduced-motion: reduce) {
.fade-enter-active,
.fade-leave-active,
.slide-enter-active,
.slide-leave-active {
transition: none !important;
}
}
實際應用場景
| 場景 | 需求 | 建議的 enter/leave 設計 |
|---|---|---|
| 彈出式對話框 | 點擊按鈕後淡入,關閉時淡出 | fade + scale 結合 appear,提升視覺焦點 |
| 側邊欄(Drawer) | 從左側滑入,滑出時隱藏 | slide + mode="out-in",避免內容閃爍 |
| 通知列(Toast) | 短暫顯示後自動淡出 | fade + transition-delay,配合 v-show |
| 資料列表的增刪 | 新增項目從下方彈出,刪除時向上滑走 | <transition-group> 搭配 list 動畫 |
| 首次載入的首頁 Banner | 首次渲染時動畫呈現 | appear + fade,或使用 JS Hook 動態控制時長 |
實務技巧:在大型專案中,可將常用動畫封裝成 全域組件(如
BaseFadeTransition.vue),只需要傳入name或duration即可重複使用,降低重複程式碼。
<!-- BaseFadeTransition.vue -->
<template>
<transition :name="name" v-bind="$attrs">
<slot />
</transition>
</template>
<script setup>
defineProps({
name: { type: String, default: 'fade' }
})
</script>
<style scoped>
.fade-enter-from,
.fade-leave-to { opacity: 0; }
.fade-enter-to,
.fade-leave-from { opacity: 1; }
.fade-enter-active,
.fade-leave-active { transition: opacity 0.3s ease; }
</style>
總結
- Vue 3 的
<transition>透過 enter / leave 類別提供了簡潔且功能強大的動畫機制,只要撰寫少量 CSS,即可為任何元素或組件加入流暢過渡。 - 理解
-from、-to、-active三階段的作用,能正確控制動畫的起始、結束與持續時間。 - 使用
name、mode、appear等屬性,配合transition-group、key、JavaScript Hook,可滿足從基礎淡入到複雜列表切換的各種需求。 - 注意常見陷阱(拼寫、缺少
key、CSS 優先度)並遵循最佳實踐(語意化類別、變數化時長、尊重 reduced‑motion),即可在專案中安全、有效地應用動畫。
掌握了 enter / leave 的運作後,你將能為 Vue 3 應用注入更具互動性的視覺效果,提升使用者體驗,同時保持程式碼的可維護性。祝開發順利,玩得開心! 🎉