Vue 3 のクラスおよびスタイルのバインディング:動的なスタイルを記述する 5 つの方法
:class と :style は、Vue で最もよく使われる 2 つのバインディングです。これらを使用すると、データに基づいて CSS クラス名やインラインスタイルを動的に切り替えることができます。本質的には、これらは v-bind の 2 つの特別な構文形式であり、非常に頻繁に使用されるため、「構文上の糖衣」として実装されています。
これら5つの記述スタイルを習得すれば、95%のケースに対応できます:オブジェクト構文、配列構文、三項演算子、計算プロパティ、文字列連結。
1. 学習内容
:classオブジェクト構文:条件に基づいて単一クラスと複数クラスを切り替える:class配列構文:複数のクラスを同時に適用する- 配列とオブジェクトの併用:複雑なクラス名の組み合わせ
:styleオブジェクト構文:動的なインラインスタイル:style配列構文:複数のスタイルオブジェクトの結合- CSS変数のバインディング(Vue 3での使用を推奨)
2. 注文ステータスラベルのスタイリングに関する課題
(1) 課題:5つの状態、5つのif文
アリスは、管理画面で5つの注文ステータスを異なる色で表示する必要がありました:
HTML
<!-- ❌ The "Flip" Version: 5 v-if with class -->
<span v-if="status === 'pending'" class="badge yellow">Pending</span>
<span v-else-if="status === 'paid'" class="badge green">Paid</span>
<span v-else-if="status === 'shipped'" class="badge blue">Shipped</span>
<span v-else-if="status === 'delivered'" class="badge gray">Delivered</span>
<span v-else class="badge red">Cancelled</span>
プロダクトマネージャーのチャーリー:
「アリス、これじゃ見苦しいよ。来月、ステータスをあと3つ追加することになったらどうする? ただコピー&ペーストするわけにはいかない。すっきりとした動的なアプローチが必要だ。」
(2) Vue :class 解答:たった1行で5つの状態を処理する
VUE
<template>
<span :class="['badge', statusClass]">{{ statusText }}</span>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({ status: String })
const statusClass = computed(() => ({
pending: 'yellow',
paid: 'green',
shipped: 'blue',
delivered: 'gray',
cancelled: 'red'
}[props.status]))
const statusText = computed(() => ({
pending: 'Pending',
paid: 'Paid',
shipped: 'Shipped',
delivered: 'Delivered',
cancelled: 'Cancelled'
}[props.status]))
</script>
(3) 収益
- HTMLの簡略化:5行のif-else → 1行の:class
- メンテナンス性:新しい状態を追加するには、
computedプロパティを変更するだけで済み、テンプレート自体は変更されません。 - 可読性:状態とクラスのマッピングが一箇所に集約されており、論理構造が明確である
3. :class の記述方法 5 選
(1) 文字列の構文
VUE
<template>
<!-- Static -->
<div class="active">Static</div>
<!-- Dynamic Strings -->
<div :class="className">Dynamic</div>
</template>
<script setup>
import { ref } from 'vue'
const className = ref('active text-bold')
</script>
(2) オブジェクト構文(最も一般的)
VUE
<template>
<!-- Basics: isActive is true, Add active class -->
<div :class="{ active: isActive }">Single</div>
<!-- Multiple conditions -->
<div :class="{
active: isActive,
'text-danger': hasError,
disabled: !canEdit
}">
Multiple
</div>
<!-- Computed properties return objects -->
<div :class="classObject">Computed</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const isActive = ref(true)
const hasError = ref(false)
const canEdit = ref(true)
// Computed Properties:Dynamic Return class Object
const classObject = computed(() => ({
active: isActive.value && !hasError.value,
'text-danger': hasError.value,
'bg-success': isActive.value
}))
</script>
(3) 配列の構文
VUE
<template>
<!-- Array: Several class concatenation -->
<div :class="[activeClass, errorClass]">Array</div>
<!-- Trinomial Expression -->
<div :class="[isActive ? 'active' : '', errorClass]">Ternary</div>
<!-- Array + Object Mixing -->
<div :class="[activeClass, { 'text-danger': hasError }]">Mixed</div>
<!-- Nested Objects in an Array -->
<div :class="[{ active: isActive }, errorClass]">Nested</div>
</template>
<script setup>
import { ref } from 'vue'
const activeClass = ref('active')
const errorClass = ref('text-danger')
const isActive = ref(true)
const hasError = ref(false)
</script>
(4) 静的クラスとの共存
VUE
<template>
<!-- Static + Dynamic Merging(Vue Automatic Merge) -->
<div class="static-class" :class="{ active: isActive }">
Both
</div>
<!-- In the end class: "static-class active" or "static-class" -->
</template>
(5) コンポーネントに関する講義
VUE
<!-- Parent Component -->
<UserCard class="shadow-lg" :class="{ active: isActive }" />
<!-- Child component UserCard.vue -->
<template>
<!-- Receive -->
<div :class="$attrs.class">
<!-- Rendering "shadow-lg active" -->
</div>
</template>
4. :style の記述方法 5 選
(1) オブジェクト構文
VUE
<template>
<!-- Basic Objects -->
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }">
Object
</div>
</template>
<script setup>
import { ref } from 'vue'
const activeColor = ref('red')
const fontSize = ref(16)
</script>
(2) CSSプロパティ名の変換
Vue は、JavaScript の命名規則を CSS の命名規則に自動的に変換します:
VUE
<template>
<div :style="{
backgroundColor: 'red', // → background-color
fontSize: '16px', // → font-size
marginTop: '10px', // → margin-top
WebkitTransform: 'scale(2)' // → -webkit-transform
}">
CSS naming
</div>
</template>
(3) 配列の構文(複数のオブジェクトの結合)
VUE
<template>
<!-- Array:Merge style objects in order(The later one overrides the earlier one)-->
<div :style="[baseStyles, overrideStyles]">Array</div>
</template>
<script setup>
import { ref } from 'vue'
const baseStyles = ref({
color: 'blue',
fontSize: '14px'
})
const overrideStyles = ref({
color: 'red', // Coverage baseStyles.color
fontWeight: 'bold'
})
</script>
(4) 自動プレフィックス付与
Vue 3 は、CSS プロパティに(ブラウザエンジンに基づいて)ブラウザプレフィックスを自動的に追加します:
JS
// Write:
{ transform: 'rotate(45deg)' }
// Vue Auto-add:
{
-webkit-transform: 'rotate(45deg)',
transform: 'rotate(45deg)'
}
(5) CSS変数(Vue 3での使用を推奨)
VUE
<template>
<div :style="{
'--main-color': mainColor,
'--spacing': spacing + 'px'
}">
CSS Variables
</div>
</template>
<script setup>
import { ref } from 'vue'
const mainColor = ref('#42b883')
const spacing = ref(20)
</script>
<style>
.dynamic {
color: var(--main-color);
padding: var(--spacing);
}
</style>
5. 完全な例:動的な注文状況
(1) ▶ サンプル:. :class の記述方法 5 通り
VUE
<template>
<!-- 1. String -->
<div :class="className">String</div>
<!-- 2. Object -->
<div :class="{ active: isActive, error: hasError }">Object</div>
<!-- 3. Array -->
<div :class="[activeClass, errorClass]">Array</div>
<!-- 4. Array + Object -->
<div :class="[activeClass, { disabled: !canEdit }]">Mixed</div>
<!-- 5. Computed Properties -->
<div :class="dynamicClass">Computed</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const isActive = ref(true)
const hasError = ref(false)
const canEdit = ref(true)
const className = ref('badge')
const activeClass = ref('active')
const errorClass = ref('error')
const dynamicClass = computed(() => ({
active: isActive.value,
error: hasError.value,
'cursor-not-allowed': !canEdit.value
}))
</script>
(2) ▶ サンプル:. :style の記述方法 5 通り
VUE
<template>
<!-- 1. Basic Objects -->
<div :style="{ color: 'red', fontSize: '16px' }">Object</div>
<!-- 2. Multiple properties -->
<div :style="{ backgroundColor: bg, padding: p + 'px' }">Multi</div>
<!-- 3. Merging Sets -->
<div :style="[baseStyles, themeStyles]">Array</div>
<!-- 4. CSS Variable -->
<div :style="{ '--theme-color': theme }">CSS Var</div>
<!-- 5. Computed Properties -->
<div :style="computedStyle">Computed</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const bg = ref('#42b883')
const p = ref(20)
const theme = ref('#35495e')
const baseStyles = ref({ color: 'white' })
const themeStyles = ref({ backgroundColor: '#42b883' })
const computedStyle = computed(() => ({
color: 'white',
backgroundColor: theme.value,
padding: `${p.value}px`
}))
</script>
(3) ▶ サンプル:. 注文ステータスバッジ
VUE
<template>
<span :class="['order-badge', statusClass]">
{{ statusText }}
</span>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
status: { type: String, required: true }
})
// 5 Status → 5 class
const statusClass = computed(() => {
const map = {
pending: 'bg-yellow',
paid: 'bg-green',
shipped: 'bg-blue',
delivered: 'bg-gray',
cancelled: 'bg-red'
}
return map[props.status] || 'bg-default'
})
const statusText = computed(() => {
const map = {
pending: 'Pending',
paid: 'Paid',
shipped: 'Shipped',
delivered: 'Delivered',
cancelled: 'Cancelled'
}
return map[props.status] || 'Unknown'
})
</script>
<style scoped>
.order-badge {
padding: 4px 12px;
border-radius: 12px;
color: white;
font-size: 12px;
}
.bg-yellow { background: #f59e0b; }
.bg-green { background: #10b981; }
.bg-blue { background: #3b82f6; }
.bg-gray { background: #6b7280; }
.bg-red { background: #ef4444; }
</style>
(4) ▶ サンプル:. よくある5つの間違い
| エラー | 症状 | 解決策 |
|---|---|---|
| String クラスが反応しない | クラスがハードコーディングされており、切り替えられない | :class を使用してバインドされている |
| 配列クラスのスペルミス | 同じ名前のクラスが複数存在し、互いに上書きし合っている | オブジェクト構文の使用 |
| スタイル単位が指定されていません | 数値「16」は 16px として解釈され、無視されます | 文字列 '16px' が出力されます |
| 誤ったCSSプロパティ名 | font-size は fontSize にする |
キャメルケースまたは引用符を使用 |
| :クラス名の競合 | 静的 + 動的な名前衝突 | Vueが自動的に統合するため問題なし |
(5) ▶ サンプル:. 5 性能比較
| 実装 | コンパイル結果 | パフォーマンス | 適用性 |
|---|---|---|---|
| 文字列 | 文字列の直接連結 | ⭐⭐⭐⭐⭐ | 簡単 |
| オブジェクト | 静的解析 | ⭐⭐⭐⭐ | 中程度 |
| 配列 | 文字列の連結 | ⭐⭐⭐⭐ | 複数のクラス |
| 配列とオブジェクト | 混合処理 | ⭐⭐⭐ | 複雑 |
| 計算プロパティ | キャッシュされた値 | ⭐⭐⭐⭐⭐ | 複雑なロジック |
(6) ▶ サンプル:. 「クラス」と「スタイル」の5つの主な違い
| 側面 | クラス | スタイル |
|---|---|---|
| 評価 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| CSSの再利用性 | 容易(クラス名の再利用) | 困難(スタイルが散在している) |
| パフォーマンス | 向上(ブラウザの最適化) | わずかに低下 |
| 優先度 | セレクタに依存 | インライン(最優先) |
| テーマの切り替え | 簡単(クラスを変更) | 簡単(CSS変数を変更) |
❓ よくある質問
Q クラスとスタイルのバインディングは共存できますか?
A はい。静的
class="x" と動的 :class="{active: y}" は自動的に統合されます(その結果、class="x active" または "x" のいずれかになります)。Q なぜ
style 属性はオブジェクトとして記述しなければならないのですか?CSS文字列を使えばいいのではないでしょうか?A はい、可能です。
<div style="color: red">はHTMLのネイティブ構文であり、<div :style="'color: red'">も文字列ベースのアプローチです。しかし、オブジェクト:style="{ color: 'red' }"を使用することで、変数を動的にバインドすることができます。オブジェクトの使用をお勧めします。Q CSS変数はどのように使いますか?
A CSS内で
{'--my-var': value}を:styleとして、var(--my-var)を:styleとして使用します。Vue 3では、テーマの切り替えにCSS変数の使用が推奨されています。これは、スタイルを直接バインドするよりも効率的だからです(1つの変数を更新するだけで済み、DOMのリフローも発生しません)。Q コンポーネントは外部クラスをどのように受け取るのですか?
A シングルルートコンポーネントは、
class 属性を自動的に継承します(Vue 3 のデフォルトの動作)。マルチルートコンポーネントは、$attrs.class を使用して特定の要素に明示的にバインドします。Q 配列構文におけるクラスの順序はどうなりますか?
A 配列内に記述された順序で連結されますが、その順序はCSSの特異性には影響しません(CSSの特異性は、CSSセレクタの特異性によって決まります)。
Q :class は v-if と一緒に使えますか?
A はい。
<div v-if="show" :class="{ active: isActive }"> show=false の場合、要素全体がレンダリングされず、isActive は効果を持ちません。Q テーマを動的に切り替えるにはどうすればよいですか?
A CSS変数の使用をお勧めします。
:style="{ '--primary': theme.primary }"はすべての要素color: var(--primary)に適用されます。テーマを切り替えるには、たった1つの変数を変更するだけで済み、100個のクラスを切り替えるよりも効率的です。📖 まとめ
:classおよび:styleは v-bind の構文糖であり、最も一般的に使用されるため、簡略化されています。- :class—定義方法5つ:文字列、オブジェクト、配列、配列+オブジェクト、計算プロパティ
- :style—5つの記述方法:オブジェクト、配列、配列+オブジェクト、CSS変数、計算プロパティ
- 静的クラスと動的 :class の自動マージ
- Vue 3 は CSS のブラウザプレフィックスを自動的に追加します
- Vue 3 でのテーマ切り替えには、CSS 変数の使用が推奨されています(スタイルを切り替えるよりも効率的です)。
- パフォーマンス:オブジェクト/計算プロパティ > 配列 > 文字列
📝 練習問題
(1) 基礎問題(難易度:⭐)
:class セレクタを使用して、3つのボタン状態を実装します:
- メイン:青色の背景に白い文字
- 成功:緑色の背景に白い文字
- 危険:赤い背景に白い文字
データ:const variant = ref('primary')
(2) 上級問題(難易度:⭐⭐)
注文ステータスに応じて、各行に異なる色のバッジが表示される注文リストを実装してください:
- 5つのステータス(保留中/支払い済み/発送済み/配達済み/キャンセル)
- 計算プロパティを使用して
classオブジェクトを返す - :style ステータスに応じたアイコンの色を追加する
(3) チャレンジ問題(難易度:⭐⭐⭐)
完全なテーマ切り替えシステムを実装する:
- 3つのテーマ(ライト/ダーク/ハイコントラスト)
- CSS変数を使用して、テーマカラー(プライマリ/セカンダリ/背景/テキスト)を紐付ける
- :class — テーマクラスの名前を切り替える
- localStorage を使ってユーザーの選択内容を記憶する
- テーマを切り替えてもページは再読み込みされません(レスポンシブデザインにより自動的に更新されます)



