タイトル: v-bindによるデータ連携
SEOタイトル: Vue.js v-bind 完全ガイド (属性 / クラス / スタイル / Composition API)
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong><code>v-bind:href="url"</code></strong> = HTML 属性に JS の値をバインド。短縮形は <strong><code>:href="url"</code></strong></li><li>クラスの動的切替: <strong><code>:class="{ active: isActive }"</code></strong> (オブジェクト) / <strong><code>:class="[cls1, cls2]"</code></strong> (配列)</li><li>スタイルの動的切替: <strong><code>:style="{ color: textColor, fontSize: size + "px" }"</code></strong></li><li><strong><code>v-bind="objWithMultipleProps"</code></strong> でオブジェクト全プロパティを一括バインド</li><li><strong>Composition API</strong> では <code>ref</code> / <code>computed</code> の値が <code>:</code> 経由で自動 unwrap</li></ul></td></tr></table>
<h2>v-bind の基本</h2>
<p><code>v-bind</code> は HTML 属性の値を JavaScript 式で動的に決定するための Vue ディレクティブです。短縮形 <code>:</code> が広く使われます。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><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></code></pre>
<h2>クラスバインディング</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><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></code></pre>
<h2>スタイルバインディング</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><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></code></pre>
<h2>オブジェクトを一括バインド</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><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></code></pre>
<h2>v-bind の修飾子</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>修飾子</th><th>意味</th><th>例</th></tr></thead>
<tbody>
<tr><td><code>.prop</code></td><td>属性ではなく DOM プロパティとしてバインド</td><td><code>:innerHtml.prop="html"</code></td></tr>
<tr><td><code>.attr</code></td><td>明示的に HTML 属性として (Vue 3)</td><td><code>:value.attr="val"</code></td></tr>
<tr><td><code>.camel</code></td><td>ケバブケース属性名をキャメルに</td><td><code>:view-box.camel="box"</code></td></tr>
<tr><td><code>.sync</code> (Vue 2)</td><td>双方向バインド (v-model:propName で代替)</td><td>非推奨</td></tr>
</tbody>
</table>
<h2>Composition API での ref / computed 連携</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript"><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></code></pre>
<h2>typical use cases</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e8f5e9"><th>シーン</th><th>v-bind の使い方</th></tr></thead>
<tbody>
<tr><td>ボタンの活性状態</td><td><code>:disabled="loading"</code></td></tr>
<tr><td>選択中タブのハイライト</td><td><code>:class="{ active: tab === current }"</code></td></tr>
<tr><td>動的画像</td><td><code>:src="userAvatar"</code></td></tr>
<tr><td>テーマカラー</td><td><code>:style="{ '--brand': brandColor }"</code></td></tr>
<tr><td>進捗バー</td><td><code>:style="{ width: percent + '%' }"</code></td></tr>
<tr><td>属性ホワイトリスト一括</td><td><code>v-bind="$attrs"</code> (透過)</td></tr>
<tr><td>外部リンク区別</td><td><code>:target="isExternal ? '_blank' : null"</code></td></tr>
</tbody>
</table>
<h2>v-bind と v-on (双方向: v-model)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-html"><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></code></pre>
<h2>よくあるミス</h2>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#fff3e0"><th>ミス</th><th>正解</th></tr></thead>
<tbody>
<tr><td><code>:href="{{ url }}"</code></td><td><code>:href="url"</code> (二重カッコ不要)</td></tr>
<tr><td><code>href="{{ url }}"</code></td><td><code>:href="url"</code> (Mustache は属性に使えない)</td></tr>
<tr><td><code>:class="active"</code> (文字列のつもり)</td><td><code>:class="'active'"</code> または <code>class="active"</code></td></tr>
<tr><td><code>:style="color: red"</code></td><td><code>:style="{ color: 'red' }"</code></td></tr>
<tr><td>true/false なのに残る</td><td>Boolean 属性に <code>v-bind</code> を使う</td></tr>
</tbody>
</table>
<h2>FAQ</h2>
<p><strong>Q: <code>v-bind</code> と <code>{{ }}</code> の違いは?</strong><br />
A: <code>{{ }}</code> (Mustache) は<strong>テキストコンテンツ</strong>専用。属性値には使えないので <code>v-bind</code> (短縮 <code>:</code>) を使います。</p>
<p><strong>Q: クラスを class と :class 両方書ける?</strong><br />
A: 可能。Vue は両者をマージします。<code><div class="base" :class="{ active: x }"></code> は OK。</p>
<p><strong>Q: <code>v-bind="$attrs"</code> って何?</strong><br />
A: 親から渡されたが props として宣言していない属性すべてを子要素にパススルー。ラッパーコンポーネントで定番。</p>