Vue3 - 樣式與 CSS 管理
主題:v-bind() 在 CSS 中使用變數
簡介
在單頁應用程式中,樣式的動態變化往往是提升使用者體驗的關鍵。例如主題切換、根據使用者操作改變顏色或間距,都是常見需求。Vue 3 提供的 v-bind()(或簡寫 :)不僅能綁定屬性,還能直接在 CSS 變數 中注入 JavaScript 的 reactive 資料,讓樣式與資料的同步變得極為簡潔。
本篇文章將說明:
- 為什麼要在 CSS 中使用
v-bind()變數 - 如何在 Vue 3 中設定與使用 CSS 變數
- 常見的陷阱與最佳實踐
適合 初學者到中級開發者,不需要先備深奧的 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中的屬性是 reactive(ref、reactive、computed),變更時樣式會即時更新。
重點:
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>
說明:點擊按鈕會切換
isDark,styleVars重新計算後,--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:結合 props 與 computed 建立可客製化的元件
<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>
說明:外部使用者可以透過
bg、color、radius三個 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 |
變更後樣式不會更新 | 確保傳入的是 ref、reactive 或 computed,若是普通常數可直接寫在 CSS 中 |
在 style 中使用 !important |
會覆蓋 v-bind 注入的變數,使其失效 |
盡量避免 !important,改用更具體的選擇器或 CSS 層級 |
| 大量變數導致樣式檔過大 | 產生冗餘 CSS,影響效能 | 只在需要動態改變的屬性上使用變數,其他靜態樣式仍寫死在 CSS 中 |
在 v-bind 物件中使用函式 |
會把函式本身當字串注入,導致無效 | 若要計算值,請在 computed 中完成,不要直接在 v-bind 物件內放函式呼叫 |
最佳實踐小結
- 集中管理變數:建議在
src/styles/theme.js(或類似檔案)中統一定義顏色、間距等主題變數,然後在元件內import。 - 使用
computed:把所有需要根據狀態變化的 CSS 變數放在computed,保持單向資料流。 - 避免過度動態化:不是所有樣式都需要透過變數控制,只有「主題色」或「尺寸」等頻繁變化的屬性才使用。
- 配合
prefers-color-scheme:可以在mounted時根據使用者系統主題自動設定theme,提升 UX。 - 測試在不同瀏覽器的支援度:CSS 變數在 IE 不支援,若需兼容請提供備援樣式或使用 PostCSS 轉譯。
實際應用場景
| 場景 | 為何選擇 v-bind() + CSS 變數 |
|---|---|
| 多主題切換(Light / Dark / 高對比) | 只需要改變幾個根變數,即可讓整個 UI 同步更新,無需重新渲染大量 DOM。 |
| 動態間距與排版 | 依據螢幕寬度或使用者自訂設定,透過變數調整 padding、margin、gap,配合 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 的響應式資料直接影響樣式,實現 資料 ↔ 樣式 的即時同步。- 只要把 reactive(
ref、reactive、computed)的物件傳給<style scoped v-bind="...">,Vue 會自動把鍵名轉為--key並注入 CSS。 - 在實務開發中,主題切換、品牌客製化、動態間距 是最常見且最適合使用此技巧的情境。
- 注意避免常見陷阱(範圍洩漏、拼寫錯誤、過度動態化),遵循最佳實踐(集中管理變數、使用
computed、只對必要屬性動態化),即可寫出 可維護、效能佳、使用者體驗高 的 Vue 3 應用。
希望本篇文章能幫助你快速上手 v-bind() 在 CSS 中使用變數,並在自己的專案裡打造出更靈活、易於維護的樣式系統。祝開發順利 🚀