v-bindは、HTMLの属性にJavaScriptのデータを結びつけるディレクティブです。画像のURL、リンク先、ボタンの有効・無効など、データによって変わる属性はほとんどこれで書きます。この記事では、Vue 3(<script setup>)を前提に、基本から、つまずきやすい挙動までを動くコードで整理します。
基本の書き方
v-bind:srcの省略形が:srcです。右辺はJavaScriptの式として評価されます。
<script setup>
import { ref } from 'vue'
const imageUrl = ref('/images/sample.png')
</script>
<template>
<!-- :src は v-bind:src の省略形 -->
<img :src="imageUrl" alt="Sample Image">
</template>
複数の属性をまとめてバインドする
引数なしのv-bindにオブジェクトを渡すと、複数の属性を一度にバインドできます。
<script setup>
import { ref } from 'vue'
const url = ref('https://example.com/')
const attrs = ref({ href: url.value, target: '_blank', rel: 'noopener' })
</script>
<template>
<a v-bind="attrs">Link</a>
</template>
個別の属性との優先順位に注意
Vue 3では、v-bindのオブジェクトと個別の属性を併用すると、後ろに書いたほうが優先されます。Vue 2では、順序に関わらず個別の属性が優先されていたため、移行時に結果が変わることがあります。
<!-- 個別指定が後ろ → id="red" -->
<div v-bind="{ id: 'blue' }" id="red"></div>
<!-- v-bind が後ろ → id="blue" -->
<div id="red" v-bind="{ id: 'blue' }"></div>
真偽属性とfalse・nullの扱い
disabledのような真偽属性は、値が真なら付与され、偽なら属性ごと削除されます。空文字はdisabled=""と同じ扱いで、付与されます。
通常の属性では、nullとundefinedを渡すと属性が削除されます。注意したいのはfalseです。Vue 3では、通常の属性にfalseを渡しても削除されず、title="false"と文字列で出力されます。Vue 2では削除されていたため、属性を消したいときはfalseではなくnullかundefinedを使ってください。
<script setup>
import { ref } from 'vue'
const isDisabled = ref(true)
const title = ref(null)
</script>
<template>
<!-- true なら disabled が付き、false なら属性ごと消える -->
<button :disabled="isDisabled">送信</button>
<!-- null / undefined なら title 属性は出力されない -->
<p :title="title">ヒント</p>
<!-- 通常の属性に false を渡すと title="false" と出力される(Vue 3) -->
<p :title="false">注意</p>
</template>
クラスとスタイルのバインディング
:classと:styleは、オブジェクトや配列を渡せる特別な属性です。たとえば:class="{ active: isActive }"のように書きます。条件の組み合わせや使い分けは動的クラスとスタイルを切り替える方法で詳しく解説しています。
動的な属性名を使う
角括弧で囲むと、属性名そのものをデータで決められます。これを動的引数と呼びます。
<script setup>
import { ref } from 'vue'
const attributeName = ref('placeholder')
</script>
<template>
<!-- 属性名もデータで決まる。null を渡すとバインドが外れる -->
<input :[attributeName]="'名前を入力'">
</template>
- 評価結果は文字列である必要があります。ただし
nullだけは例外で、バインドを外す意味になります。 - HTMLに直接書くタイプのテンプレート(in-DOMテンプレート)では、ブラウザが属性名を小文字に変換するため、大文字を含む名前は期待どおりに動きません。単一ファイルコンポーネント(SFC)では問題ありません。
コンポーネントのpropsへ値を渡す
コンポーネントに対しても、同じ書き方でpropsに値を渡せます。テンプレートではprop-oneのようにケバブケース、子側の定義ではpropOneのようにキャメルケースで書くのが慣例です。
<!-- 親コンポーネント -->
<MyComponent :prop-one="value" />
<!-- 子コンポーネント(MyComponent.vue) -->
<script setup>
defineProps({
propOne: String
})
</script>
同名の省略記法(Vue 3.4以降)
属性名と変数名が同じなら、値を省略できます。:idは:id="id"と同じ意味です。Vue 3.4より前のバージョンでは使えません。
<script setup>
import { ref } from 'vue'
const id = ref('main')
</script>
<template>
<!-- Vue 3.4 以降::id は :id="id" と同じ -->
<div :id></div>
</template>
href・srcに外部の値をバインドするときの注意
ユーザーが入力したURLを:hrefにそのまま渡すと、javascript:で始まるURLにより、クリック時にスクリプトが実行されるおそれがあります。
<script setup>
// userUrl は外部から来る値(例:プロフィールのWebサイトURL)
defineProps({ userUrl: String })
</script>
<template>
<!-- javascript: で始まるURLだと、クリックでスクリプトが実行されるおそれがある -->
<a :href="userUrl">サイトを見る</a>
</template>
Vue公式は、ユーザーが入力したURLは、データベースに保存する前にバックエンドでサニタイズすることを基本にしています。そうすればAPIを使うすべてのクライアントが守られます。フロントエンド側のサニタイズに頼る設計は、すでにセキュリティ上の問題を抱えているとされています。また、サニタイズしても、リンク先が安全な場所かどうかまではVueでは保証できません。
関連するディレクティブ
- 入力値の双方向バインディングは双方向データバインディングでフォームの入力値を反映させる方法
- イベントの購読はv-onでDOMイベントを購読する方法
- 条件による表示切り替えはv-ifで条件分岐させる方法
- リスト表示はv-forで配列やオブジェクトをループして表示する方法
よくある質問(FAQ)
disabledのような真偽属性は消えます。通常の属性は、Vue 3ではtitle="false"と出力され、消えません。属性を消したい場合はnullかundefinedを渡してください。:srcはv-bind:srcの省略形で、同じ意味です。まとめ
v-bindは、属性にデータを結びつける基本のディレクティブです。Vue 3では、falseが属性を消さないこと、v-bindのオブジェクトが宣言順で優先されること、同名の省略記法が3.4以降であることを押さえておくと、思わぬ挙動に悩まされません。外部から来るURLは、バックエンドでのサニタイズが前提です。
