Vue3 教學:模板語法(Template Syntax)— 動態 style 綁定
簡介
在 Vue3 中,**樣式(style)**是 UI 呈現的核心要素之一。傳統的 CSS 靜態寫法在面對需要根據資料狀態即時變化的介面時,往往顯得力不從心。Vue 的 動態 style 綁定 讓開發者可以直接在模板中根據組件的資料或計算屬性(computed)來調整元素的外觀,實現「資料驅動」的 UI。
掌握動態 style 綁定不只可以讓程式碼更簡潔,還能提升可維護性與重用性。無論是根據使用者互動改變背景顏色、根據螢幕尺寸調整字體大小,或是實作主題切換(dark / light mode),都是日常開發中常見的需求。本篇文章將從基本語法、實作範例、常見陷阱,到最佳實踐與真實案例,完整說明 Vue3 中的動態 style 綁定。
核心概念
1. :style 簡寫與物件語法
Vue 在模板中提供 v-bind:style(簡寫為 :style)指令,用來把 JavaScript 物件 直接映射為行內樣式。物件的 key 為 CSS 屬性(使用駝峰式或字串),value 為對應的值。
<div :style="{ color: textColor, fontSize: fontSize + 'px' }">
動態文字樣式
</div>
textColor、fontSize為組件內的 data 或 computed 屬性。- 若屬性值是 數字,Vue 會自動加上
px(但僅限於部分屬性),為了保險起見,建議手動加上單位。
2. 陣列語法(多個樣式物件合併)
有時候樣式需要根據多個來源合併,例如基礎樣式 + 狀態樣式。此時可以使用 陣列,Vue 會依序合併每個物件,後面的會覆蓋前面的同名屬性。
<div :style="[baseStyle, hoverStyle, activeStyle]">
多層樣式合併
</div>
3. 使用計算屬性(computed)產生樣式物件
若樣式邏輯較為複雜,直接寫在模板會讓程式碼雜亂。計算屬性可以把樣式封裝起來,保持模板的可讀性。
export default {
data() {
return {
isError: false,
progress: 0
};
},
computed: {
progressBarStyle() {
return {
width: this.progress + '%',
backgroundColor: this.isError ? 'red' : 'green'
};
}
}
};
<div class="progress" :style="progressBarStyle"></div>
4. 動態 CSS 變數(CSS Custom Properties)
Vue 也支援 CSS 變數 的綁定,讓樣式的變化更具彈性。只要在樣式表中使用 var(--my-var),再在模板中透過 :style 設定變數即可。
<div class="card" :style="{ '--card-bg': cardBg }">
使用 CSS 變數的卡片
</div>
.card {
background: var(--card-bg, #fff);
padding: 1rem;
border-radius: 8px;
}
5. 內聯樣式 vs. class 綁定
雖然 :style 方便快速變更單一屬性,但若樣式較多或需要支援偽類(:hover、:active)時,class 綁定(:class)仍是首選。本文仍以 :style 為主,並在「常見陷阱」中說明何時切換。
程式碼範例
以下示範 5 個常見且實用的動態 style 綁定情境,皆附上完整說明。
範例 1️⃣ 基本物件綁定:變色按鈕
<template>
<button
:style="{ backgroundColor: btnColor, color: '#fff', padding: '0.5rem 1rem' }"
@click="toggleColor"
>
點我切換顏色
</button>
</template>
<script setup>
import { ref } from 'vue'
const btnColor = ref('steelblue')
function toggleColor() {
btnColor.value = btnColor.value === 'steelblue' ? 'tomato' : 'steelblue'
}
</script>
- 說明:
btnColor為ref,點擊時切換顏色,樣式即時更新。
範例 2️⃣ 陣列合併樣式:卡片懸停效果
<template>
<div
class="card"
:style="[baseStyle, isHover ? hoverStyle : {}]"
@mouseenter="isHover = true"
@mouseleave="isHover = false"
>
Hover 我看見變化
</div>
</template>
<script setup>
import { ref } from 'vue'
const isHover = ref(false)
const baseStyle = {
width: '200px',
height: '120px',
backgroundColor: '#f5f5f5',
transition: 'transform 0.3s, box-shadow 0.3s'
}
const hoverStyle = {
transform: 'translateY(-5px)',
boxShadow: '0 8px 20px rgba(0,0,0,0.15)'
}
</script>
<style scoped>
.card {
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
}
</style>
- 說明:使用陣列把
baseStyle與條件式hoverStyle合併,達成乾淨的懸停動畫。
範例 3️⃣ 計算屬性產生進度條樣式
<template>
<div class="progress-wrapper">
<div class="progress-bar" :style="barStyle"></div>
</div>
<input type="range" min="0" max="100" v-model="progress" />
</template>
<script setup>
import { ref, computed } from 'vue'
const progress = ref(45)
const barStyle = computed(() => ({
width: `${progress.value}%`,
backgroundColor: progress.value > 80 ? '#ff5252' : '#42b983',
height: '100%'
}))
</script>
<style scoped>
.progress-wrapper {
width: 100%;
height: 20px;
background: #e0e0e0;
border-radius: 10px;
overflow: hidden;
margin-bottom: 1rem;
}
.progress-bar {
transition: width 0.2s ease;
}
</style>
- 說明:
progress透過滑桿調整,barStyle會即時計算寬度與顏色。
範例 4️⃣ CSS 變數結合主題切換
<template>
<div class="theme-box" :style="{ '--bg': currentBg, '--txt': currentTxt }">
<p>這是一段文字,會跟著主題變色。</p>
<button @click="toggleTheme">切換主題</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const isDark = ref(false)
const currentBg = computed(() => (isDark.value ? '#333' : '#fff'))
const currentTxt = computed(() => (isDark.value ? '#eee' : '#222'))
function toggleTheme() {
isDark.value = !isDark.value
}
</script>
<style scoped>
.theme-box {
background: var(--bg);
color: var(--txt);
padding: 1.5rem;
border-radius: 6px;
text-align: center;
}
button {
margin-top: 1rem;
}
</style>
- 說明:透過
--bg、--txt兩個 CSS 變數,實作簡易的暗黑 / 亮色切換。
範例 5️⃣ 多屬性條件綁定:表單驗證提示
<template>
<form @submit.prevent="handleSubmit">
<input
v-model="email"
placeholder="請輸入 Email"
:style="emailStyle"
/>
<span v-if="emailError" class="error-msg">Email 格式不正確</span>
<button type="submit">送出</button>
</form>
</template>
<script setup>
import { ref, computed } from 'vue'
const email = ref('')
const emailError = ref(false)
const emailStyle = computed(() => ({
border: emailError.value ? '2px solid #ff5252' : '1px solid #ccc',
padding: '0.4rem',
borderRadius: '4px'
}))
function handleSubmit() {
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
emailError.value = !pattern.test(email.value)
if (!emailError.value) {
alert('送出成功!')
}
}
</script>
<style scoped>
.error-msg {
color: #ff5252;
font-size: 0.85rem;
margin-left: 0.5rem;
}
</style>
- 說明:根據驗證結果動態改變
border樣式,給使用者即時的視覺回饋。
常見陷阱與最佳實踐
| 陷阱 | 為什麼會發生 | 解決方案 |
|---|---|---|
| 樣式寫成字串但忘記加單位 | width: 100 會被當作 100px(部分屬性)或直接失效 |
始終使用字串 + 單位(如 '100px')或在計算屬性中自行拼接。 |
在 :style 內直接寫函式呼叫 |
每次渲染都會重新建立物件,造成效能浪費 | 把邏輯抽到 computed 或 methods,返回已緩存的物件。 |
| 過度依賴行內樣式 | 難以維護、無法使用偽類、媒體查詢等 | 樣式較多時改用 :class,只保留少量動態屬性於 :style。 |
| 物件鍵名使用 kebab-case | 在 JavaScript 物件中必須使用字串或駝峰式,否則會觸發語法錯誤 | 使用駝峰式(backgroundColor)或把鍵名寫成字串('background-color')。 |
| 樣式衝突時不易除錯 | 多個陣列物件同時設定同一屬性,難以判斷最終值 | 在開發階段使用 Vue Devtools 查看最終渲染的 style,或在計算屬性內明確設定優先順序。 |
最佳實踐
- 保持樣式物件純粹:只放與 UI 直接相關的屬性,避免把業務邏輯混在一起。
- 使用
computed產生樣式物件:可自動快取,減少不必要的重新渲染。 - 將變化大的屬性(如動畫)抽成 CSS class,搭配
:class動態切換。 - 適度使用 CSS 變數:可在全局主題或多元元件間共享樣式值。
- 在大型專案中建立「樣式工廠」(style helper)函式,統一管理顏色、間距等設計系統變數。
實際應用場景
- 即時儀表板(Dashboard)
- 根據資料變化改變圖表顏色、警示框背景、文字粗細。
- 暗黑模式切換
- 透過 CSS 變數與
:style結合,一鍵切換全站配色。
- 透過 CSS 變數與
- 表單驗證即時回饋
- 錯誤時把
border、background改為警示色,提升使用者體驗。
- 錯誤時把
- 漸層或動畫效果
- 利用
:style動態改變backgroundImage、transform,產生互動式過場。
- 利用
- 多語系或客製化 UI
- 不同語系可能需要不同字體大小或行高,使用
:style依語系自動調整。
- 不同語系可能需要不同字體大小或行高,使用
總結
動態 style 綁定是 Vue3 提供的 資料驅動 UI 的核心能力之一。透過 :style、陣列合併、計算屬性與 CSS 變數,我們可以在模板中直接以 JavaScript 表達樣式邏輯,實現即時、可維護且彈性的介面變化。
- 掌握基本語法:物件與陣列寫法是最常用的兩種方式。
- 善用 computed:將複雜邏輯抽離,提升效能與可讀性。
- 避免常見陷阱:記得加單位、避免過度行內樣式、利用 Devtools 觀察最終結果。
- 結合 CSS 變數:讓主題切換與全局樣式管理更為簡潔。
只要熟練上述概念與最佳實踐,您就能在 Vue3 專案中靈活地控制每一個視覺細節,打造出既美觀又具互動性的使用者體驗。祝您寫程式寫得開心,介面寫得更棒!