【Vue.js】属性バインディングの使い方

v-bindは、HTMLの属性にJavaScriptのデータを結びつけるディレクティブです。画像のURL、リンク先、ボタンの有効・無効など、データによって変わる属性はほとんどこれで書きます。この記事では、Vue 3(<script setup>)を前提に、基本から、つまずきやすい挙動までを動くコードで整理します。

スポンサーリンク

基本の書き方

v-bind:srcの省略形が:srcです。右辺はJavaScriptの式として評価されます。

Vue:基本のバインディング
<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にオブジェクトを渡すと、複数の属性を一度にバインドできます。

Vue:オブジェクトで複数の属性をバインド
<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では、順序に関わらず個別の属性が優先されていたため、移行時に結果が変わることがあります。

Vue:宣言順で優先が決まる
<!-- 個別指定が後ろ → 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を使ってください。

Vue:真偽属性とnull・falseの違い
<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 }"のように書きます。条件の組み合わせや使い分けは動的クラスとスタイルを切り替える方法で詳しく解説しています。

動的な属性名を使う

角括弧で囲むと、属性名そのものをデータで決められます。これを動的引数と呼びます。

Vue:動的な属性名
<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のようにキャメルケースで書くのが慣例です。

Vue:propsへのバインディング
<!-- 親コンポーネント -->
<MyComponent :prop-one="value" />

<!-- 子コンポーネント(MyComponent.vue) -->
<script setup>
defineProps({
  propOne: String
})
</script>

同名の省略記法(Vue 3.4以降)

属性名と変数名が同じなら、値を省略できます。:idは:id="id"と同じ意味です。Vue 3.4より前のバージョンでは使えません。

Vue:同名の省略記法
<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により、クリック時にスクリプトが実行されるおそれがあります。

Vue:外部のURLをバインドする例(注意が必要)
<script setup>
// userUrl は外部から来る値(例:プロフィールのWebサイトURL)
defineProps({ userUrl: String })
</script>

<template>
  <!-- javascript: で始まるURLだと、クリックでスクリプトが実行されるおそれがある -->
  <a :href="userUrl">サイトを見る</a>
</template>

Vue公式は、ユーザーが入力したURLは、データベースに保存する前にバックエンドでサニタイズすることを基本にしています。そうすればAPIを使うすべてのクライアントが守られます。フロントエンド側のサニタイズに頼る設計は、すでにセキュリティ上の問題を抱えているとされています。また、サニタイズしても、リンク先が安全な場所かどうかまではVueでは保証できません。

関連するディレクティブ

よくある質問(FAQ)

Q:falseを渡すと属性が消えますか?
Adisabledのような真偽属性は消えます。通常の属性は、Vue 3ではtitle="false"と出力され、消えません。属性を消したい場合はnullかundefinedを渡してください。
Qv-bindとコロンの書き方に違いはありますか?
A違いはありません。:srcはv-bind:srcの省略形で、同じ意味です。
Qv-bindのオブジェクトと個別の属性が重なったら、どちらが優先されますか?
AVue 3では、後ろに書いたほうが優先されます。Vue 2では個別の属性が常に優先だったため、移行の際は順序を見直してください。
Q:[attributeName]で大文字を含む属性名が動かないのはなぜですか?
AHTMLに直接書くテンプレート(in-DOMテンプレート)では、ブラウザが属性名を小文字に変換するためです。単一ファイルコンポーネントや文字列テンプレートで書けば問題ありません。

まとめ

v-bindは、属性にデータを結びつける基本のディレクティブです。Vue 3では、falseが属性を消さないこと、v-bindのオブジェクトが宣言順で優先されること、同名の省略記法が3.4以降であることを押さえておくと、思わぬ挙動に悩まされません。外部から来るURLは、バックエンドでのサニタイズが前提です。