本文 AI 產出,尚未審核

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-ifv-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 屬性

如果同時出現 enterleave(例如列表切換),預設兩者會同時執行。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 觸發 enterleavefade-enter-fromfade-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 / leave JavaScript hook 完全自訂動畫。


常見陷阱與最佳實踐

陷阱 說明 解決方案
類別名稱拼寫錯誤 -enter-from-enter-to 拼寫不一致會導致動畫失效。 使用 IDE 自動補全或在 name 後寫註解提醒。
忘記設定 transition 時間 只寫 -from / -to 而未加 -active,瀏覽器不會平滑過渡。 必須在 -active 中明確設定 transitionanimation
v-ifv-show 混用 v-show 只改變 display,不觸發 enter / leave 想要動畫時,使用 v-ifappear
列表過渡缺少 key Vue 無法辨識哪個項目是新增或刪除,導致不正確的動畫。 為每個 v-for 項目提供唯一且穩定的 :key
mode 使用不當 同時進出動畫時,未設定 mode 可能產生閃爍或重疊。 根據 UI 需求選擇 out-inin-out 或保留預設。
過度使用 transition-group 大量項目同時動畫會造成效能下降。 使用 requestAnimationFrame 或限制同時動畫數量。
CSS 優先度衝突 全局樣式覆寫了 scoped 動畫類別。 使用 scoped::v-deep 或提升特異性 (!important 應慎用)。

最佳實踐

  1. 保持類別語意化:使用 fade-slide- 等前綴,讓樣式易於維護。
  2. 將動畫抽離為 SCSS/LESS 變數:如 $transition-duration: 0.3s;,統一管理。
  3. 使用 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),只需要傳入 nameduration 即可重複使用,降低重複程式碼。

<!-- 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 三階段的作用,能正確控制動畫的起始、結束與持續時間。
  • 使用 namemodeappear 等屬性,配合 transition-groupkey、JavaScript Hook,可滿足從基礎淡入到複雜列表切換的各種需求。
  • 注意常見陷阱(拼寫、缺少 key、CSS 優先度)並遵循最佳實踐(語意化類別、變數化時長、尊重 reduced‑motion),即可在專案中安全、有效地應用動畫。

掌握了 enter / leave 的運作後,你將能為 Vue 3 應用注入更具互動性的視覺效果,提升使用者體驗,同時保持程式碼的可維護性。祝開發順利,玩得開心! 🎉