本文 AI 產出,尚未審核

Vue3 - 樣式與 CSS 管理

主題:v-bind() 在 CSS 中使用變數


簡介

在單頁應用程式中,樣式的動態變化往往是提升使用者體驗的關鍵。例如主題切換、根據使用者操作改變顏色或間距,都是常見需求。Vue 3 提供的 v-bind()(或簡寫 :)不僅能綁定屬性,還能直接在 CSS 變數 中注入 JavaScript 的 reactive 資料,讓樣式與資料的同步變得極為簡潔。

本篇文章將說明:

  1. 為什麼要在 CSS 中使用 v-bind() 變數
  2. 如何在 Vue 3 中設定與使用 CSS 變數
  3. 常見的陷阱與最佳實踐

適合 初學者到中級開發者,不需要先備深奧的 CSS 預處理器知識,只要了解 Vue 的響應式概念即可上手。


核心概念

1. CSS 變數(Custom Properties)簡介

CSS 變數(又稱 Custom Properties)以 --variable-name 的形式宣告,並可在任何支援的屬性值中使用 var(--variable-name) 取得。它們的特點:

  • 層疊:可在 :root、父元素或局部元素上宣告,子元素會自動繼承。
  • 可變:可在 JavaScript 中透過 element.style.setProperty('--name', value) 動態修改,變更會即時反映到渲染結果。

範例

:root {
  --primary-color: #42b983;
}
.btn {
  background-color: var(--primary-color);
}

2. v-bind()<style> 標籤中的使用

Vue 3 支援在 單檔元件(SFC)<style> 標籤上加上 v-bind,語法如下:

<style scoped v-bind="styleVars">
  .card {
    background-color: var(--bg-color);
    color: var(--text-color);
    padding: var(--spacing);
  }
</style>
  • v-bind="styleVars":會把 styleVars 這個物件的鍵值對映射成 CSS 變數(鍵名自動加上 -- 前綴)。
  • 只要 styleVars 中的屬性是 reactiverefreactivecomputed),變更時樣式會即時更新。

重點v-bind 只能在 <style> 標籤內使用,且必須配合 scoped(或不加 scoped)才能正確產生唯一的 CSS 作用域。

3. 建立響應式的樣式變數

<script setup> 中,我們通常會這樣寫:

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

// 主題顏色
const theme = ref('light')   // 'light' 或 'dark'

// 依據主題計算 CSS 變數
const styleVars = computed(() => ({
  // 注意:鍵名會自動加上 `--`
  bgColor: theme.value === 'light' ? '#ffffff' : '#1e1e1e',
  textColor: theme.value === 'light' ? '#333333' : '#f0f0f0',
  spacing: '1.2rem'
}))
</script>

styleVars 會在 theme 變更時重新計算,v-bind 會把結果注入 <style>,完成 資料 ↔ 樣式 的雙向綁定。


程式碼範例

以下提供 五個實用範例,從最基礎到較進階的情境,說明如何在 Vue 3 中使用 v-bind() 於 CSS 變數。

範例 1:最簡單的顏色綁定

<template>
  <button @click="toggle">切換主題</button>
  <div class="box">我是盒子</div>
</template>

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

const isDark = ref(false)
const toggle = () => (isDark.value = !isDark.value)

const styleVars = computed(() => ({
  bgColor: isDark.value ? '#2c3e50' : '#ecf0f1',
  textColor: isDark.value ? '#ecf0f1' : '#2c3e50'
}))
</script>

<style scoped v-bind="styleVars">
.box {
  width: 200px;
  height: 100px;
  background-color: var(--bg-color);
  color: var(--text-color);
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

說明:點擊按鈕會切換 isDarkstyleVars 重新計算後,--bg-color--text-color 立即更新,盒子的樣式同步變化。

範例 2:使用 ref 直接綁定單一變數

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

const primary = ref('#42b983')
</script>

<style scoped v-bind="{ primary }">
.btn {
  background: var(--primary);
  color: #fff;
  padding: 0.6rem 1.2rem;
  border: none;
  border-radius: 4px;
}
</style>

說明v-bind="{ primary }" 會把 primary 直接映射為 --primary,適合只有少數變數的情況。

範例 3:結合 propscomputed 建立可客製化的元件

<template>
  <div class="card">
    <slot />
  </div>
</template>

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

defineProps({
  bg: { type: String, default: '#fff' },
  color: { type: String, default: '#333' },
  radius: { type: String, default: '8px' }
})

const styleVars = computed(() => ({
  bgColor: props.bg,
  textColor: props.color,
  borderRadius: props.radius
}))
</script>

<style scoped v-bind="styleVars">
.card {
  background: var(--bg-color);
  color: var(--text-color);
  border-radius: var(--border-radius);
  padding: 1rem;
  box-shadow: 0 2px 6px rgba(0,0,0,.1);
}
</style>

說明:外部使用者可以透過 bgcolorradius 三個 props 自訂樣式,元件內部只需要關心 styleVars 的計算。

範例 4:主題切換 + 動畫過渡

<template>
  <div class="theme-toggle" @click="toggle">
    切換主題
  </div>
  <section class="content">
    <h2>動態主題</h2>
    <p>顏色、間距、陰影全部跟著變。</p>
  </section>
</template>

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

const theme = ref('light')
const toggle = () => (theme.value = theme.value === 'light' ? 'dark' : 'light')

const styleVars = computed(() => ({
  bgColor: theme.value === 'light' ? '#fafafa' : '#2d2d2d',
  textColor: theme.value === 'light' ? '#222' : '#eaeaea',
  shadow: theme.value === 'light' ? '0 4px 12px rgba(0,0,0,.08)' : '0 4px 12px rgba(0,0,0,.5)',
  spacing: theme.value === 'light' ? '1rem' : '1.5rem'
}))
</script>

<style scoped v-bind="styleVars">
.theme-toggle {
  cursor: pointer;
  padding: var(--spacing);
  background: var(--bg-color);
  color: var(--text-color);
  text-align: center;
  margin-bottom: 1rem;
}
.content {
  background: var(--bg-color);
  color: var(--text-color);
  padding: var(--spacing);
  box-shadow: var(--shadow);
  transition: all 0.3s ease;
}
</style>

說明:透過 transition,在主題切換時可看到平滑過渡的效果。所有樣式皆來源於 styleVars,維護成本低。

範例 5:在 setup() 中使用 useCssModule(結合 CSS Modules)

<template>
  <button :class="$style.btn" @click="increase">+1</button>
  <p :class="$style.counter">計數:{{ count }}</p>
</template>

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

const count = ref(0)
const increase = () => count.value++

const $style = useCssModule()

const styleVars = computed(() => ({
  btnBg: count.value % 2 === 0 ? '#3498db' : '#e74c3c',
  btnColor: '#fff'
}))
</script>

<style module scoped v-bind="styleVars">
.btn {
  background: var(--btn-bg);
  color: var(--btn-color);
  border: none;
  padding: 0.6rem 1rem;
  border-radius: 4px;
}
.counter {
  margin-top: 0.8rem;
  font-size: 1.2rem;
}
</style>

說明:即使使用 CSS Modules,只要在 <style> 加上 v-bind,仍可將 reactive 變數注入到模組化的 class 中,實現 模組化 + 動態樣式 的雙贏。


常見陷阱與最佳實踐

陷阱 可能的結果 解決方式 / 最佳實踐
忘記加 scoped 變數會洩漏到全局,導致其他元件樣式被意外覆寫 總是在需要限定範圍的元件中使用 scoped,或明確規劃全局變數於 :root
變數名稱拼寫錯誤 (var(--bg-color) vs --bg-colour) 样式不會套用,且在 DevTools 中難以發現 使用 IDE 的自動補全或在 styleVars 物件中統一管理鍵名
將非 reactive 資料傳入 v-bind 變更後樣式不會更新 確保傳入的是 refreactivecomputed,若是普通常數可直接寫在 CSS 中
style 中使用 !important 會覆蓋 v-bind 注入的變數,使其失效 盡量避免 !important,改用更具體的選擇器或 CSS 層級
大量變數導致樣式檔過大 產生冗餘 CSS,影響效能 只在需要動態改變的屬性上使用變數,其他靜態樣式仍寫死在 CSS 中
v-bind 物件中使用函式 會把函式本身當字串注入,導致無效 若要計算值,請在 computed 中完成,不要直接在 v-bind 物件內放函式呼叫

最佳實踐小結

  1. 集中管理變數:建議在 src/styles/theme.js(或類似檔案)中統一定義顏色、間距等主題變數,然後在元件內 import
  2. 使用 computed:把所有需要根據狀態變化的 CSS 變數放在 computed,保持單向資料流。
  3. 避免過度動態化:不是所有樣式都需要透過變數控制,只有「主題色」或「尺寸」等頻繁變化的屬性才使用。
  4. 配合 prefers-color-scheme:可以在 mounted 時根據使用者系統主題自動設定 theme,提升 UX。
  5. 測試在不同瀏覽器的支援度:CSS 變數在 IE 不支援,若需兼容請提供備援樣式或使用 PostCSS 轉譯。

實際應用場景

場景 為何選擇 v-bind() + CSS 變數
多主題切換(Light / Dark / 高對比) 只需要改變幾個根變數,即可讓整個 UI 同步更新,無需重新渲染大量 DOM。
動態間距與排版 依據螢幕寬度或使用者自訂設定,透過變數調整 paddingmargingap,配合 CSS Grid / Flex 更彈性。
品牌客製化 SaaS 平台允許客戶上傳自訂配色,只要把配色寫入 styleVars,所有元件自動套用。
動畫與過渡 變更變數值時,CSS transition 會自動平滑過渡,如主題切換、按鈕點擊的顏色變化。
暗黑模式自動偵測 透過 window.matchMedia('(prefers-color-scheme: dark)') 初始化 theme,之後所有樣式皆受 styleVars 控制。

範例:在大型儀表板(Dashboard)中,使用者可以在「設定」頁面選擇品牌色、字體大小,這些設定會存入 Vuex/Pinia,然後透過 v-bind 注入到全局 CSS 變數,所有圖表、表格、卡片立即跟著變更,提升開發與維護效率。


總結

  • v-bind() 搭配 CSS Custom Properties,讓 Vue 3 的響應式資料直接影響樣式,實現 資料 ↔ 樣式 的即時同步。
  • 只要把 reactiverefreactivecomputed)的物件傳給 <style scoped v-bind="...">,Vue 會自動把鍵名轉為 --key 並注入 CSS。
  • 在實務開發中,主題切換、品牌客製化、動態間距 是最常見且最適合使用此技巧的情境。
  • 注意避免常見陷阱(範圍洩漏、拼寫錯誤、過度動態化),遵循最佳實踐(集中管理變數、使用 computed、只對必要屬性動態化),即可寫出 可維護、效能佳、使用者體驗高 的 Vue 3 應用。

希望本篇文章能幫助你快速上手 v-bind() 在 CSS 中使用變數,並在自己的專案裡打造出更靈活、易於維護的樣式系統。祝開發順利 🚀