本文 AI 產出,尚未審核

Vue3 教學:樣式與 CSS 管理 – 動態 class / style 綁定


簡介

在單頁應用程式 (SPA) 中,樣式的切換與變化往往是使用者互動的關鍵。Vue3 透過 v-bind:classv-bind:style(簡寫為 :class:style)提供了直觀且高效的動態 CSS 綁定機制,讓開發者可以根據資料狀態即時改變元素的外觀,而不必在程式碼中手動操作 DOM。

本單元將深入探討 Vue3 的動態 class 與 style 綁定,說明其語法、背後原理,並提供多個實務範例,協助你在開發過程中 快速、乾淨地管理樣式,提升程式碼可讀性與維護性。


核心概念

1. 為什麼使用動態綁定?

  • 資料驅動:Vue 的響應式系統會在資料變化時自動更新樣式,避免手動 DOM 操作。
  • 條件式樣式:可以根據布林值、計算屬性或陣列/物件的內容,決定套用哪一個 class。
  • 可維護性:樣式邏輯集中於 Vue 組件的 data / computed,樣式名稱與 CSS 本身保持分離。

2. :class 的三種寫法

寫法 說明 範例
字串 直接寫 class 名稱,適合單一固定 class。 :class="'active'"
陣列 多個 class,支援條件式 (三元運算子) 或計算屬性。 :class="[isRed ? 'red' : '', sizeClass]"
物件 key 為 class 名稱,value 為布林值,值為 true 時套用。 :class="{ active: isActive, disabled: isDisabled }"

小技巧:在 script setup 中使用 computed 產生物件或陣列,可讓模板保持簡潔。

3. :style 的兩種寫法

寫法 說明 範例
字串 直接寫 CSS 文字,適合單一屬性。 :style="'color: red;'"
物件 key 為 CSS 屬性(駝峰或 kebab-case),value 為對應值。 :style="{ backgroundColor: bgColor, fontSize: fontSize + 'px' }"

注意:若屬性名稱使用 kebab-case,必須加上引號(例如 '"font-weight"'),或直接使用駝峰式 fontWeight

4. 計算屬性與方法的配合

使用 computed 可以把「樣式邏輯」抽離出來,使模板更具可讀性:

<script setup>
import { ref, computed } from 'vue'

const isActive = ref(false)
const size = ref('md')   // 'sm' | 'md' | 'lg'

const buttonClass = computed(() => ({
  btn: true,
  'btn--active': isActive.value,
  [`btn--${size.value}`]: true   // 動態產生 btn--sm / btn--md / btn--lg
}))
</script>

<template>
  <button :class="buttonClass">按鈕</button>
</template>

程式碼範例

下面提供 5 個實用範例,涵蓋字串、陣列、物件、計算屬性與混合使用的情境。每個範例皆附上說明與註解。

範例 1:簡單字串綁定(單一 class)

<template>
  <!-- 當 isError 為 true 時,加上 error 類別 -->
  <p :class="isError ? 'error' : ''">{{ message }}</p>
</template>

<script setup>
import { ref } from 'vue'

const isError = ref(true)
const message = ref('資料載入失敗')
</script>

<style>
.error {
  color: #e74c3c;
  font-weight: bold;
}
</style>

重點:使用三元運算子直接在模板中決定 class,適合條件簡單的情況。


範例 2:陣列綁定 + 多條件

<template>
  <div :class="badgeClasses">Badge</div>
  <button @click="toggle()">切換狀態</button>
</template>

<script setup>
import { ref, computed } from 'vue'

const isSuccess = ref(false)
const isWarning = ref(true)

const badgeClasses = computed(() => [
  'badge',                               // 永遠套用
  isSuccess.value && 'badge--success',  // 成功時
  isWarning.value && 'badge--warning'   // 警告時
].filter(Boolean))   // 移除 false
</script>

<style>
.badge { padding: 4px 8px; border-radius: 4px; }
.badge--success { background: #2ecc71; color: #fff; }
.badge--warning { background: #f1c40f; color: #fff; }
</style>

技巧filter(Boolean) 可以自動把 falsenullundefined 移除,避免產生空字串。


範例 3:物件綁定(最常用)

<template>
  <button :class="btnClass" @click="isActive = !isActive">
    {{ isActive ? '已啟用' : '未啟用' }}
  </button>
</template>

<script setup>
import { ref, computed } from 'vue'

const isActive = ref(false)

const btnClass = computed(() => ({
  btn: true,
  'btn--active': isActive.value,
  'btn--disabled': !isActive.value
}))
</script>

<style>
.btn { padding: 8px 16px; border: none; cursor: pointer; }
.btn--active { background: #3498db; color: #fff; }
.btn--disabled { background: #bdc3c7; color: #2c3e50; }
</style>

說明:物件的 key 為 class 名稱,value 為布林值,Vue 只會在值為 true 時套用對應的 class。


範例 4:動態 style 物件 + 單位處理

<template>
  <div :style="boxStyle" class="box"></div>
  <input type="range" min="50" max="300" v-model="size" /> 大小
</template>

<script setup>
import { ref, computed } from 'vue'

const size = ref(150)          // 直接綁定在 input 上
const bgColor = ref('#e67e22')

const boxStyle = computed(() => ({
  width: size.value + 'px',
  height: size.value + 'px',
  backgroundColor: bgColor.value,
  borderRadius: `${size.value / 10}px`
}))
</script>

<style>
.box {
  transition: all 0.3s ease;
  margin-top: 16px;
}
</style>

重點:在 style 物件中自行拼接單位(如 px%),或使用模板字串讓程式碼更易讀。


範例 5:結合 :class:style 的複合範例(卡片切換)

<template>
  <section class="card" :class="cardState" :style="cardStyle">
    <h3>{{ title }}</h3>
    <p>{{ content }}</p>
    <button @click="toggle()">切換模式</button>
  </section>
</template>

<script setup>
import { ref, computed } from 'vue'

const title = ref('動態卡片')
const content = ref('這是一張可以切換樣式的卡片。')
const isDark = ref(false)

const cardState = computed(() => ({
  'card--dark': isDark.value,
  'card--light': !isDark.value
}))

const cardStyle = computed(() => ({
  // 文字顏色根據主題自動改變
  color: isDark.value ? '#ecf0f1' : '#2c3e50',
  // 背景使用漸層
  background: isDark.value
    ? 'linear-gradient(135deg, #34495e, #2c3e50)'
    : 'linear-gradient(135deg, #fff, #f8f9fa)'
}))
function toggle() {
  isDark.value = !isDark.value
}
</script>

<style>
.card {
  padding: 24px;
  border-radius: 8px;
  transition: all 0.4s ease;
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.card--dark { border: 1px solid #2c3e50; }
.card--light { border: 1px solid #dcdcdc; }
</style>

實務應用:在主題切換、狀態指示、交互式動畫等情境中,同時使用 class 與 style 可讓樣式更具彈性與可讀性。


常見陷阱與最佳實踐

陷阱 說明 解決方案
直接在模板寫長字串 大量條件會讓模板變得雜亂。 把 class 物件或陣列抽成 computed,保持模板簡潔。
忘記加單位 width: 100 會被視為 100px? 不是,瀏覽器會忽略。 在 style 物件中自行拼接單位,或使用 Number + 'px'
使用 kebab-case 時忘記加引號 :style="{ 'font-size': size + 'px' }" 必須加引號。 建議統一使用駝峰式 (fontSize) 以避免錯誤。
過度依賴 v-if 取代 class 切換 每次切換都會重新渲染 DOM,效能下降。 盡量使用 class / style 綁定,僅在結構真的需要變化時才使用 v-if
樣式衝突 多個 class 同時套用時,優先順序不易掌握。 使用 BEM 命名法或 CSS Modules,確保權重可預測。

最佳實踐

  1. 保持樣式與邏輯分離:CSS 放在 <style>(或獨立檔案),Vue 只負責決定 個 class 被套用。
  2. 使用 computed:把判斷式集中在 computed,讓模板只呈現「資料」而非「邏輯」。
  3. 命名一致:採用 BEM 或 CSS Modules,避免全域樣式污染。
  4. 避免內嵌過多 style:除非是動態屬性(如寬度、顏色),否則盡量使用 class。
  5. 利用 transition:配合 :class 的切換,可直接使用 CSS transitionanimation,實現流暢過渡。

實際應用場景

場景 為何適合使用動態 class / style
表單驗證提示 根據驗證結果切換 error / success class,顯示不同顏色與圖示。
暗黑模式切換 只改變根元素的 theme--dark / theme--light class,子元件自動繼承樣式。
即時圖表或儀表板 根據資料值切換顏色(如 greenred),或調整寬度/高度以呈現比例。
多語系 UI 依語系切換字體或排版方向(rtl / ltr)的 class。
動畫與過渡 使用 enter-activeleave-active 等 class 搭配 transition,在條件變化時自動觸發動畫。

總結

動態 class 與 style 綁定是 Vue3 中最實用的樣式管理方式。透過 :class(字串、陣列、物件)與 :style(字串、物件)配合 計算屬性,我們可以:

  • 根據資料狀態即時切換樣式,不必手動操作 DOM。
  • 保持模板簡潔,將判斷邏輯集中於 computed
  • 提升可維護性,讓 CSS 與邏輯分離,方便團隊協作。
  • 減少效能損耗,避免不必要的重新渲染或樣式衝突。

掌握上述概念與實務技巧後,你將能在 各種 UI 互動、主題切換、即時資料視覺化 場景中,快速且優雅地控制樣式,寫出 乾淨、可讀、易維護 的 Vue3 應用程式。祝你開發順利,玩得開心!