タイトル: v-bindによるデータ連携
SEOタイトル: Vue.js v-bind 完全ガイド (属性 / クラス / スタイル / Composition API)
| この記事の要点 |
|
v-bind の基本
v-bind は HTML 属性の値を JavaScript 式で動的に決定するための Vue ディレクティブです。短縮形 : が広く使われます。
<template>
<!-- 完全形 -->
<a v-bind:href="url">リンク</a>
<!-- 短縮形 (推奨) -->
<a :href="url">リンク</a>
<!-- 複数の属性 -->
<img :src="imageUrl" :alt="imageAlt" :width="width">
<!-- 動的な属性名 (Vue 2.6+) -->
<button :[attrName]="attrValue">動的</button>
<!-- Boolean 属性 (true なら属性追加、false なら削除) -->
<button :disabled="isLoading">送信</button>
<input :readonly="!editable">
</template>
<script setup>
import { ref } from 'vue';
const url = ref('https://example.com');
const imageUrl = ref('/img/logo.png');
const isLoading = ref(false);
</script>
クラスバインディング
<template>
<!-- オブジェクト構文: キー=クラス名、値=真偽値 -->
<div :class="{ active: isActive, disabled: !enabled }">...</div>
<!-- 配列構文 -->
<div :class="[baseClass, themeClass]">...</div>
<!-- 配列内オブジェクト -->
<div :class="[baseClass, { active: isActive }]">...</div>
<!-- 三項演算子 -->
<div :class="isError ? 'text-red' : 'text-green'">...</div>
<!-- computed と組み合わせ -->
<div :class="classObject">...</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const isActive = ref(true);
const enabled = ref(false);
const baseClass = ref('btn');
const themeClass = ref('btn-primary');
const classObject = computed(() => ({
active: isActive.value,
'text-danger': hasError.value,
}));
</script>
スタイルバインディング
<template>
<!-- オブジェクト構文(キーはキャメルケース or ケバブケース) -->
<div :style="{ color: textColor, fontSize: size + 'px' }">...</div>
<div :style="{ 'background-color': bg }">...</div>
<!-- 配列で複数オブジェクト -->
<div :style="[baseStyles, overrideStyles]">...</div>
<!-- CSS 変数 (CSS Custom Properties) -->
<div :style="{ '--main-color': mainColor }">...</div>
<!-- ベンダープレフィックス自動付与 -->
<div :style="{ transform: 'translateX(' + offset + 'px)' }">...</div>
<!-- 配列値で fallback -->
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }">...</div>
</template>
<script setup>
import { ref } from 'vue';
const textColor = ref('#333');
const size = ref(16);
const offset = ref(100);
</script>
オブジェクトを一括バインド
<template>
<!-- 個別 -->
<input :id="form.id" :type="form.type" :value="form.value" :name="form.name">
<!-- 一括バインド (v-bind に引数なし) -->
<input v-bind="form">
<!-- スプレッド的に使える -->
<BlogPost v-bind="post" />
<!-- 上記は <BlogPost :id="post.id" :title="post.title" ... /> と等価 -->
</template>
<script setup>
import { ref } from 'vue';
const form = ref({
id: 'username',
type: 'text',
value: 'taro',
name: 'user',
});
const post = ref({
id: 1,
title: 'はじめての Vue.js',
author: '田中',
});
</script>
v-bind の修飾子
| 修飾子 | 意味 | 例 |
|---|---|---|
.prop | 属性ではなく DOM プロパティとしてバインド | :innerHtml.prop="html" |
.attr | 明示的に HTML 属性として (Vue 3) | :value.attr="val" |
.camel | ケバブケース属性名をキャメルに | :view-box.camel="box" |
.sync (Vue 2) | 双方向バインド (v-model:propName で代替) | 非推奨 |
Composition API での ref / computed 連携
<script setup>
import { ref, computed, reactive } from 'vue';
// ref: プリミティブの reactive ラッパー
const count = ref(0);
const isAdmin = ref(false);
// テンプレート内では自動 unwrap (.value 不要)
// <p :data-count="count"> ← count.value ではなく count
// computed: 派生値
const doubled = computed(() => count.value * 2);
const labelClass = computed(() => ({
active: count.value > 0,
danger: count.value > 100,
}));
// reactive: オブジェクトの reactive ラッパー
const user = reactive({
name: '田中',
age: 30,
});
// 関数で動的な値
function buttonClass(idx) {
return {
active: idx === selectedIdx.value,
first: idx === 0,
};
}
</script>
<template>
<p :data-count="count">{{ doubled }}</p>
<span :class="labelClass">ラベル</span>
<div :data-user-name="user.name">...</div>
<button v-for="(item, i) in items" :class="buttonClass(i)">
{{ item }}
</button>
</template>
typical use cases
| シーン | v-bind の使い方 |
|---|---|
| ボタンの活性状態 | :disabled="loading" |
| 選択中タブのハイライト | :class="{ active: tab === current }" |
| 動的画像 | :src="userAvatar" |
| テーマカラー | :style="{ '--brand': brandColor }" |
| 進捗バー | :style="{ width: percent + '%' }" |
| 属性ホワイトリスト一括 | v-bind="$attrs" (透過) |
| 外部リンク区別 | :target="isExternal ? '_blank' : null" |
v-bind と v-on (双方向: v-model)
<template>
<!-- v-bind + v-on で擬似 v-model -->
<input :value="name" @input="name = $event.target.value">
<!-- v-model はその短縮 (Vue 3) -->
<input v-model="name">
<!-- 子コンポーネントへの v-model (Vue 3) -->
<CustomInput v-model="form.email" v-model:label="form.label" />
<!-- 子側: defineProps(['modelValue', 'label']) + emit('update:modelValue', val) -->
</template>
よくあるミス
| ミス | 正解 |
|---|---|
:href="{{ url }}" | :href="url" (二重カッコ不要) |
href="{{ url }}" | :href="url" (Mustache は属性に使えない) |
:class="active" (文字列のつもり) | :class="'active'" または class="active" |
:style="color: red" | :style="{ color: 'red' }" |
| true/false なのに残る | Boolean 属性に v-bind を使う |
FAQ
Q: v-bind と {{ }} の違いは?
A: {{ }} (Mustache) はテキストコンテンツ専用。属性値には使えないので v-bind (短縮 :) を使います。
Q: クラスを class と :class 両方書ける?
A: 可能。Vue は両者をマージします。<div class="base" :class="{ active: x }"> は OK。
Q: v-bind="$attrs" って何?
A: 親から渡されたが props として宣言していない属性すべてを子要素にパススルー。ラッパーコンポーネントで定番。