🐷

Vueのデータバインディングについて調べてみた

に公開

Vueのデータバインディングについて

Vue.jsにおけるバインド(bind)とは、一言で言うと「データと見た目(DOM)を結びつける(連携させる)仕組み」のことです。これをデータバインディングと呼びます。

データバインディングを使うと、JavaScriptのデータ(変数やオブジェクト)が変更されたときに、Webページの表示が自動的に更新されるようになります。これにより、DOMを直接操作するコードを書く手間が省け、コードがシンプルで分かりやすくなります。

主要なバインドにはいくつか種類があります。


テキストのバインド(マスタッシュ構文)

最も基本的なバインド方法で、{{ }}(マスタッシュ構文)を使ってデータをテキストとして表示します。

データのmessageが変更されると、<h1>タグの中身も自動的に更新されます。

コード例:

<div id="app">
  <h1>{{ message }}</h1>
</div>

<script>
  const { createApp } = Vue

  createApp({
    data() {
      return {
        message: 'こんにちは、Vue!'
      }
    }
  }).mount('#app')
</script>

表示結果:

こんにちは、Vue!


属性のバインド (v-bind)

v-bind は、HTML要素の属性(attribute)を Vue インスタンスのデータと動的に連携させるためのディレクティブです。これにより、データの変更に応じて要素の見た目や動作をリアルタイムに制御できます。

一般的には省略形の :(コロン) がよく使われます。例えば、v-bind:href:href と同じ意味です。


基本的な使い方

属性値にVueのデータプロパティを割り当てる最もシンプルな使い方です。

<div id="app">
  <img :src="imageUrl" alt="Vue Logo">
  
  <button :disabled="isButtonDisabled">クリックしてね</button>
</div>

<script>
  const { createApp } = Vue

  createApp({
    data() {
      return {
        imageUrl: 'https://vuejs.org/images/logo.png',
        isButtonDisabled: true // trueだとボタンは無効になる
      }
    }
  }).mount('#app')
</script>

isButtonDisabled の値を false に変更すると、ボタンがクリックできるようになります。


クラスのバインディング

v-bind の中でも特に強力で頻繁に使われるのが、class 属性の動的な操作です。オブジェクト構文と配列構文の2つの方法があります。

1. オブジェクト構文

{ クラス名: 真偽値 } の形式で記述します。真偽値が true の場合にのみ、そのクラスが要素に適用されます。

コード例:

<div id="app">
  <div :class="{ active: isActive, 'text-danger': hasError }">
    このテキストの色やスタイルが変わります
  </div>
  
  <button @click="toggleActive">アクティブ切り替え</button>
  <button @click="toggleError">エラー切り替え</button>
</div>

<style>
  .active {
    font-weight: bold;
    color: blue;
  }
  .text-danger {
    background-color: #fdd;
    border: 1px solid red;
  }
</style>

<script>
  // ... Vueインスタンスの作成
  createApp({
    data() {
      return {
        isActive: true,
        hasError: false
      }
    },
    methods: {
      toggleActive() { this.isActive = !this.isActive },
      toggleError() { this.hasError = !this.hasError }
    }
  }).mount('#app')
</script>

この方法を使うと、複数のクラスの有無をそれぞれのデータプロパティで個別に管理できるため、非常に便利です。

2. 配列構文

複数のクラスをリストとして適用したい場合に使用します。

コード例:

<div id="app">
  <div :class="[activeClass, errorClass]">テキスト</div>

  <p :class="[isActive ? activeClass : '', errorClass]">条件付きテキスト</p>
</div>

<script>
  // ... Vueインスタンスの作成
  createApp({
    data() {
      return {
        activeClass: 'active',
        errorClass: 'text-danger',
        isActive: true
      }
    }
  }).mount('#app')
</script>

配列構文とオブジェクト構文は組み合わせて使うこともできます。
<div :class="[{ active: isActive }, errorClass]">...</div>


スタイルのバインディング

インラインの style 属性を動的に操作する場合にも v-bind を使います。こちらもオブジェクト構文と配列構文があります。

1. オブジェクト構文

CSSプロパティをキーとするオブジェクトを渡します。プロパティ名はキャメルケース (fontSize) またはケバブケース ('font-size') で指定できます。

コード例:

<div id="app">
  <p :style="{ color: activeColor, fontSize: fontSize + 'px' }">
    このテキストのスタイルはデータで管理されています
  </p>
  
  <input type="range" v-model="fontSize" min="10" max="40">
</div>

<script>
  // ... Vueインスタンスの作成
  createApp({
    data() {
      return {
        activeColor: 'red',
        fontSize: 16
      }
    }
  }).mount('#app')
</script>

スライダーを動かすと、リアルタイムで文字の大きさが変わります。

2. 配列構文

複数のスタイルオブジェクトをマージ(結合)して適用できます。

<div id="app">
  <p :style="[baseStyles, overridingStyles]">
    複数のスタイルオブジェクトを適用
  </p>
</div>

<script>
  // ... Vueインスタンスの作成
  createApp({
    data() {
      return {
        baseStyles: {
          color: 'white',
          backgroundColor: 'black',
          padding: '10px'
        },
        overridingStyles: {
          fontWeight: 'bold',
          border: '2px solid orange'
        }
      }
    }
  }).mount('#app')
</script>

動的な引数

v-bind の少し高度な使い方として、バインドする属性名自体を動的にすることができます。角括弧 [] を使って属性名をデータで指定します。

コード例:

<div id="app">
  <p>
    <a :[attributeName]="url">動的な属性を持つリンク</a>
  </p>

  <button @click="changeAttribute">属性を変更</button>
</div>

<script>
  // ... Vueインスタンスの作成
  createApp({
    data() {
      return {
        attributeName: 'href',
        url: 'https://jp.vuejs.org/'
      }
    },
    methods: {
      changeAttribute() {
        // 'href' と 'title' を交互に切り替える
        this.attributeName = this.attributeName === 'href' ? 'title' : 'href'
      }
    }
  }).mount('#app')
</script>

ボタンをクリックすると、リンクが機能したり、マウスオーバーでツールチップが表示されたりする動作が切り替わります。

まとめ

v-bind (または :) は、Vue.jsでUIを動的に制御するための基本かつ非常に重要な機能です。

  • 基本的な属性: src, href, disabled などをデータと連動させます。
  • クラスの操作: オブジェクトや配列を使い、複雑なCSSクラスの適用ロジックを簡潔に記述できます。
  • スタイルの操作: インラインスタイルをデータで管理し、動的なデザイン変更を容易にします。
  • 動的な引数: 属性名自体もデータで管理できるため、高い柔軟性を持ちます。

特にクラスとスタイルのバインディングは、インタラクティブなUIを作成する上で必須のテクニックとなります。


フォーム入力のバインド (v-model)

v-modelは、フォームの入力要素 (<input>, <textarea>, <select>) やコンポーネントと、Vueインスタンスの**データを双方向で結びつける(バインドする)**ためのディレクティブです。


双方向データバインディングとは?

v-modelの最大の特徴は「双方向」である点です。

  1. データ → 表示: JavaScriptのデータ (data) を変更すると、フォームの表示(値)が自動的に更新されます。
  2. 表示 → データ: ユーザーがフォームに入力・操作すると、その内容が自動的にJavaScriptのデータ (data) に反映されます。

この仕組みにより、面倒なDOM操作やイベントリスナーの設定をすることなく、フォームの状態をデータとして簡単に管理できます。


要素別の使い方

v-modelは、使用するフォーム要素によって少しだけ挙動が変わります。

1. テキスト入力 (<input type="text">, <textarea>)

最も基本的な使い方です。入力された文字列がそのままデータに反映されます。

<div id="app">
  <input type="text" v-model="message" placeholder="メッセージを入力">
  <p>入力内容: {{ message }}</p>
</div>

<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        message: ''
      }
    }
  }).mount('#app');
</script>

入力ボックスに文字をタイプすると、<p> タグの表示がリアルタイムで更新されていきます。

2. チェックボックス (<input type="checkbox">)

単一の場合:
単一のチェックボックスは、真偽値(true/false)を扱います。

<div id="app">
  <input type="checkbox" id="agreement" v-model="agreed">
  <label for="agreement">利用規約に同意します</label>
  <p>同意の状態: {{ agreed }}</p>
</div>

<script>
  // ...
  createApp({
    data() { return { agreed: false } }
  }).mount('#app');
</script>

チェックを入れると agreedtrue に、外すと false になります。

複数の場合:
複数のチェックボックスを同じデータ(配列)にバインドすることで、選択された値をまとめて管理できます。

<div id="app">
  <p>好きなフルーツを選んでください:</p>
  <input type="checkbox" id="apple" value="りんご" v-model="checkedFruits">
  <label for="apple">りんご</label>
  
  <input type="checkbox" id="orange" value="みかん" v-model="checkedFruits">
  <label for="orange">みかん</label>
  
  <input type="checkbox" id="grape" value="ぶどう" v-model="checkedFruits">
  <label for="grape">ぶどう</label>
  
  <p>選択中のフルーツ: {{ checkedFruits }}</p>
</div>

<script>
  // ...
  createApp({
    data() { return { checkedFruits: [] } } // 配列で初期化
  }).mount('#app');
</script>

チェックを入れた項目の value 属性の値が、checkedFruits 配列に追加/削除されます。

3. ラジオボタン (<input type="radio">)

複数の選択肢から一つだけを選ぶ場合に使います。選択されたラジオボタンの value 属性の値がデータに格納されます。

<div id="app">
  <p>配送方法:</p>
  <input type="radio" id="mail" value="メール便" v-model="shippingMethod">
  <label for="mail">メール便</label>
  
  <input type="radio" id="express" value="宅配便" v-model="shippingMethod">
  <label for="express">宅配便</label>
  
  <p>選択中の配送方法: {{ shippingMethod }}</p>
</div>

<script>
  // ...
  createApp({
    data() { return { shippingMethod: 'メール便' } }
  }).mount('#app');
</script>

4. セレクトボックス (<select>)

ドロップダウンリストの選択値をバインドします。選択された <option>value 属性の値がデータに格納されます。

<div id="app">
  <select v-model="selectedCategory">
    <option disabled value="">カテゴリーを選択してください</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <p>選択中のカテゴリー: {{ selectedCategory }}</p>
</div>

<script>
  // ...
  createApp({
    data() { return { selectedCategory: '' } }
  }).mount('#app');
</script>

v-modelの修飾子(Modifiers)

v-model の動作を細かく調整するための修飾子があります。

修飾子 説明 使用例
.lazy input イベントごとではなく、change イベントが起きた後(フォーカスが外れた時など)にデータを同期させます。 <input v-model.lazy="msg">
.number ユーザーの入力値を自動的に数値 (Number) 型に変換します。 <input v-model.number="age">
.trim ユーザー入力の前後の空白を自動的に除去します。 <input v-model.trim="username">

これらの修飾子は、v-model.lazy.trim="text" のように繋げて使うこともできます。


v-model の仕組み(内部的な動き)

v-model は、実は **v-bindv-on を組み合わせた糖衣構文(シンタックスシュガー)**です。つまり、内部的には以下のコードと同じ働きをしています。

<input v-model="searchText">

<input
  :value="searchText"
  @input="searchText = $event.target.value"
>
  • :value="searchText" (v-bind): データ (searchText) を input 要素の value 属性にバインドします(データ → 表示)。
  • @input="..." (v-on): input イベントが発生するたび(文字が入力されるたび)に、イベントオブジェクト ($event) から入力値を取得し、データ (searchText) に代入します(表示 → データ)。

この仕組みを理解しておくと、後述するカスタムコンポーネントで v-model を使う際に非常に役立ちます。


カスタムコンポーネントでの v-model

v-model は自作のコンポーネントに対しても使用でき、再利用性の高い入力コンポーネントを作成するのに非常に便利です。

Vue 3では、コンポーネントで v-model を使うと、デフォルトで以下のようになります。

  • プロパティ(props): modelValue という名前のプロパティを受け取ります。
  • イベント(emits): 値を変更するとき、update:modelValue という名前のイベントを親に通知(emit)します。

例: カスタム入力コンポーネント

CustomInput.js (子コンポーネント)

app.component('custom-input', {
  props: ['modelValue'],
  emits: ['update:modelValue'],
  template: `
    <input
      :value="modelValue"
      @input="$emit('update:modelValue', $event.target.value)"
    >
  `
})

親コンポーネントでの使用

<div id="app">
  <custom-input v-model="parentMessage"></custom-input>
  <p>親のデータ: {{ parentMessage }}</p>
</div>

<script>
  // ...
  createApp({
    data() { return { parentMessage: '初期値' } }
  }).mount('#app');
</script>

このように、親子間でのデータの受け渡しが v-model 一つで非常にシンプルに記述できます。


イベントのバインド (v-on)

v-on は、DOMイベント(ユーザーの操作など)を監視し、イベントが発生した時に指定されたJavaScriptコードを実行するためのディレクティブです。

一般的には、より短く記述できる省略形の @ が広く使われます。例えば、v-on:click@click と全く同じ意味です。


基本的な使い方

v-on は、@ の後にイベント名(click, submit, input など)を続け、実行したい処理を値として指定します。

1. メソッドを呼び出す

最も一般的な使い方です。methods オプションに定義した関数(メソッド)を呼び出します。

<div id="app">
  <p>カウント: {{ count }}</p>
  
  <button @click="increment">カウントアップ</button>
</div>

<script>
  const { createApp } = Vue;

  createApp({
    data() {
      return {
        count: 0
      }
    },
    methods: {
      // ボタンがクリックされたときに実行される処理
      increment() {
        this.count++;
        console.log('カウントが1増えました。');
      }
    }
  }).mount('#app');
</script>

2. インラインで処理を記述する

非常にシンプルな処理であれば、methods を定義せずに直接テンプレート内に記述することも可能です。

<div id="app">
  <p>カウント: {{ count }}</p>
  
  <button @click="count++">カウントアップ</button>
</div>

メソッドに引数を渡す

イベントハンドラに独自の引数を渡すことができます。

1. 独自の引数を渡す

メソッド名の後の () 内に引数を指定します。

<div id="app">
  <button @click="greet('Hello')">挨拶する</button>
</div>

<script>
  // ...
  createApp({
    methods: {
      greet(message) {
        alert(message);
      }
    }
  }).mount('#app');
</script>

2. 元のDOMイベントオブジェクトも使う

独自の引数を使いつつ、元のDOMイベントオブジェクトにもアクセスしたい場合は、特別な変数 $event を引数として渡します。

<div id="app">
  <button @click="warn('ボタンがクリックされました', $event)">警告を表示</button>
</div>

<script>
  // ...
  createApp({
    methods: {
      warn(message, event) {
        // event はネイティブの DOM イベントオブジェクト
        if (event) {
          event.preventDefault(); // イベントのデフォルトの動作をキャンセル
        }
        alert(message);
        console.log(event.target.tagName); // 'BUTTON' が出力される
      }
    }
  }).mount('#app');
</script>

イベント修飾子(Event Modifiers)

v-on には、イベントの動作を細かく制御するための「修飾子」があります。イベント名の後にドット . をつけて記述します。これらを使うことで、event.preventDefault() のような定型コードを書く手間が省けます。

修飾子 説明
.stop イベントの伝播を停止します (event.stopPropagation())。
.prevent 要素のデフォルトの動作をキャンセルします (event.preventDefault())。
.capture イベントをキャプチャフェーズで処理します。
.self イベントがその要素自身から発生した場合のみハンドラをトリガーします(子要素からの伝播は無視)。
.once ハンドラを一度だけ実行し、その後はトリガーされません。
.passive イベントのデフォルト動作を妨げないことをブラウザに伝え、特にスクロールイベントなどでパフォーマンスを向上させます。

使用例: .prevent.stop

<form @submit.prevent="onSubmit">
  <button type="submit">送信</button>
</form>

<div @click="handleParentClick">
  親要素
  <button @click.stop="handleChildClick">子要素 (クリックしても親のイベントは発生しない)</button>
</div>

キー修飾子(Key Modifiers)

キーボードイベント (@keydown, @keyupなど) を扱う際に、特定のキーが押された時だけハンドラを実行するように指定できます。

一般的なキーエイリアス:

  • .enter
  • .tab
  • .delete (DeleteBackspace の両方のキーを捕捉)
  • .esc
  • .space
  • .up, .down, .left, .right

使用例:

<div id="app">
  <input @keydown.enter="addItem" v-model="newItem" placeholder="アイテムを追加してEnter">
</div>

これにより、「もし event.key が 'Enter' なら...」といった条件分岐を書く必要がなくなります。


カスタムイベントの待受

v-on (または @) は、子コンポーネントから発行(emit)されたカスタムイベントを親コンポーネントで受け取るためにも使われます。これはコンポーネント間の通信で非常に重要な役割を果たします。

ChildComponent.js (子コンポーネント)

app.component('child-component', {
  emits: ['response'], // 発行するイベント名を明記
  template: `
    <button @click="$emit('response', '子からのメッセージです!')">
      親にイベントを通知
    </button>
  `
})

親コンポーネントでの使用

<div id="app">
  <child-component @response="handleResponse"></child-component>
  <p>{{ childMessage }}</p>
</div>

<script>
  // ...
  createApp({
    data() {
      return { childMessage: '' }
    },
    methods: {
      handleResponse(payload) {
        this.childMessage = payload; // 子から受け取ったデータを更新
      }
    }
  }).mount('#app');
</script>

このように、v-on はDOMイベントからコンポーネント間の通信まで、Vueアプリケーションのあらゆる「イベント」を扱うための統一されたインターフェースを提供します。

まとめ

バインドの種類 ディレクティブ(省略形) 説明 用途
テキスト {{ }} データをテキストとして表示する(単方向) 画面へのデータ表示
属性 v-bind (:) HTMLの属性値とデータを結びつける(単方向) srcclassの動的な変更
フォーム入力 v-model フォーム要素とデータを双方向で結びつける ユーザー入力のハンドリング
イベント v-on (@) DOMイベントとメソッドを結びつける クリックなどのユーザー操作への対応

これらのバインドを使いこなすことで、Vue.jsのリアクティブ(反応的)なWebアプリケーションを効率的に開発できます。

GitHubで編集を提案

Discussion