前々回の記事では、条件に応じて要素の表示を切り替える「v-if」と「v-show」について紹介しました。
そして前回の記事では、配列やオブジェクトに格納されたデータを元に要素を繰り返し描画する「v-for」とHTML属性を連動させる「v-bind」について紹介しました。
今回の記事では、Vueアプリケーションをインタラクティブにするために、ユーザーの操作に応答し、よりリッチなWebアプリケーションを作るための「v-on」と「v-model」を記事にしたいと思います。前提としてこの記事では、Vue3での記法を前提とします。
- v-onについて理解する。
- v-modelについて理解する。
v-on:ユーザーのアクションを捉える
Webページをインタラクティブにするには、ユーザーの操作(クリック、マウスオーバー、キーボード入力など)を検知して、それに応じた処理を実行する必要があります。その役割を担うのが「v-on」ディレクティブです。Vueでは、「v-on」ディレクティブを使ってDOMを監視し、イベント発生したときに、特定のJavascriptコードを実行します。
イベントリスニングの基本と省略形
イベントリスニングとは、の発生を待ってから応答する処理の仕組みのことで、処理の関数のことをイベントリスナーと呼びます。
「v-on」では「v-on:イベント名」という構文で、イベントリスナーを設定します。
<button v-on:click="counter += 1">Add 1</button>
「v-bind」と同様に、「v-on」にも非常によく使われる省略形があります。それがアットマーク (@) です。v-on:clickは@clickと書くことができます。この省略形は簡潔で読みやすいため、実際の開発では標準的に使われます 。
<script setup> function sayHello() { alert('こんにちは!') } </script> <template> <button @click="sayHello">挨拶する</button> </template>
この例では、ボタンがクリックされるとsayHelloメソッドが実行されます。
メソッドハンドラ と インラインハンドラ
「@click」に指定する処理には、2つの書き方があります。
- インラインハンドラ
- メソッドハンドラ
インラインハンドラとは
簡単なJavascript式をテンプレートに直接記述する方法で、単純な処理の場合の利用にはこちらが適しています。
<script setup> import { ref } from 'vue' const counter = ref(0) </script> <template> <p> {{ counter }} </p> <button @click="counter++">Click!</button> </template>
上記のように、@click="counter++"とテンプレートのbutton要素内に記述する方法になります。
メソッドハンドラとは
コンポーネントのmethodsオプションで定義したメソッド名を指定する方法。コードが整理され、再利用性も高まるため、ロジックが複雑になる場合は、こちらの利用が適しています。
<script setup> import { ref } from 'vue' const counter = ref(0) function sayHello() { counter.value++ } </script> <template> <button @click="sayHello">挨拶する</button> {{ counter }} </template>
上記のように、@clickでsayHelloメソッドを呼び出しています。
イベント情報を取得する方法
発生したイベントに関わる様々な情報を取得した場合、イベントオブジェクトと呼ばれるイベントハンドラーおよびイベントリスナーにおいて実行される関数の引数として受け取ることのできるオブジェクトを使って取得することができます。
<script setup> import { ref } from 'vue' const test = ref(0) function sayHello(event) { alert('こんにちは!') console.log(event) } </script> <template> <button @click="sayHello">挨拶する</button> <button @click="test = $event.clientX">クリック位置</button> {{ test }} </template>
上記のサンプルコードのようにsayHellor関数の第1引数に「event」と入力をするか、button要素のインラインイベントの中に「$event」と記述をすることで、イベント情報のオブジェクトを取得することができます。
イベントハンドラーに引数を渡す
<script setup> <script setup> function sayHello(event, time) { alert('こんにちは!' + time + '時です') console.log(event) } </script> <template> <button @click="sayHello($event, 5)">挨拶する</button> </template>
javascriptの関数を実行する際に引数を渡すケースというのは非常に多いと思います。v-onディレクティブでイベントハンドラーに引数を渡す場合は、呼び出し時に、$eventの次に渡したい値を設定することで関数に値を渡すことができます。
イベント修飾子を使って処理を簡略化する
イベントハンドラ内では、「event.preventDefault()(デフォルトの動作をキャンセル)」や「event.stopPropagation()(イベントの伝播を停止)」といった処理が頻繁に必要になります。これらの処理をメソッド内に毎回書くのは冗長ですし、メソッドの本来の目的(ビジネスロジック)から逸れてしまいます。
この問題を解決するため、Vueは「イベント修飾子」という非常にスマートな機能を提供しています。これは、@ディレクティブの末尾にドット (.) を付けて追加する接尾辞です
宣言的なイベント管理
イベント修飾子の本質は、DOMイベントに関する定型的な処理を、JavaScriptのメソッドからテンプレートの宣言へと移すことにあります。
<script setup> import { ref } from 'vue' const counter = ref(0) </script> <template> <div @click="counter++"> <button @click.stop="">ボタン</button> </div> {{ counter }} </template>
これにより、onClickメソッドは「counter値をカウントアップする」という処理から解放されます。これはVueの設計思想である「関心の分離」を体現した、非常に強力な機能です。
よく使われる修飾子
| 修飾子 | 説明 |
|---|---|
| .stop | イベントの伝播を停止します (event.stopPropagation()) 。 |
| .prevent | 要素のデフォルトの動作を抑制します (event.preventDefault()) 。 |
| .self | イベントがその要素自身から発生した場合にのみハンドラをトリガします(子要素からのバブリングではトリガしない) 。 |
| .once | ハンドラを最大で1回だけトリガします 。 |
| .passive | イベントのデフォルト動作を決して抑制しないことをブラウザに伝え、特にモバイルでのスクロール性能を向上させます。.preventとは同時に使えません 。 |
キー修飾子とシステム修飾子
キーボード操作時に特定のキーが押された時にだけ処理を実行したい場合もVueでは修飾子を用意しています。
- キー修飾子: @keyup.enter="submit"のように、特定のキー名を修飾子として使えます 。
- システム修飾子: @click.ctrl="doSomething"のように、ctrl、alt、shiftキーが押されていることを条件にできます 。
| 修飾子 | 説明 | 使用例 |
|---|---|---|
| .prevent | 要素のデフォルトの動作を抑制する。 | form @submit.prevent="onSubmit" |
| .stop | イベントの伝播(バブリング)を停止する。 | div @click.stop="doThis" |
| .once | イベントハンドラを一度だけ実行する。 | button @click.once="doOnce" |
| .self | イベントが自分自身から発生した時のみ実行する。 | div @click.self="doThat" |
| .enter | Enterキーが押された時に実行する。 | input @keyup.enter="submit" |
| .ctrl | Ctrlキーを押しながらイベントが発生した時に実行する。 | div @click.ctrl="onCtrlClick" |
| .passive | スクロールイベントのパフォーマンスを向上させる。 | div @scroll.passive="onScroll" |
v-model:双方向データバインディング
フォームの入力要素とコンポーネントのデータを双方向にバインドする際、「v-model」ディレクティブを使います。双方向データバインディングとは、フォームの入力要素(inputなど)の値と、Vueのデータが常に同期する仕組みのことです。ユーザーがフォームに何かを入力すれば、即座にデータが更新されます。逆に、プログラム側でデータを変更すれば、即座にフォームの表示も更新されます。
<script setup> import { ref } from 'vue' const message = ref('') const checked = ref(false) </script> <template> <input v-model="message" placeholder="メッセージを入力" /> <p>メッセージ: {{ message }}</p> <input id="checkbox" v-model="checked" type="checkbox" /> <label for="checkbox">{{ checked }}</label> </template>
様々な入力要素での使い方
「v-model」は、入力要素の種類に応じて賢く動作を変えます 。
- テキスト入力(type="text"・textarea) : valueプロパティとinputイベントを使用します。
- チェックボックス(type="checkbox"): ・単一の場合:真偽値(true/false)にバインドします 。 ・複数の場合:同じデータ配列にバインドすることで、選択されたvalueが配列に追加/削除されます 。
- ラジオボタン (type="radio"): 選択されたラジオボタンのvalueをデータにバインドします 。
- セレクトボックス (select): 選択された
便利な修飾子
「v-model」にも、動作を微調整するための修飾子があります。
- 「.lazy」: inputイベントごとではなく、changeイベント(フォーカスが外れた時など)の後にデータを同期させます 。
- 「.number」: 入力値を自動的に数値型に変換しようと試みます 。
- 「.trim」: 入力値の前後の空白を自動的に除去します 。
まとめ
- 第1回では、v-ifとv-showでアプリケーションの構造を動的に変える方法を学びました。
- 第2回では、v-forとv-bindを使ってデータを元にUIを生成し、その見た目をリアクティブに制御する方法を探求しました。
- 第3回では、v-onとv-modelでユーザーとの対話を可能にし、カスタムディレクティブでVueの可能性をさらに広げました。
これらのディレクティブを組み合わせることで、静的なHTMLだけでは作れなかった、動的でリッチなユーザーインターフェースを構築できるようになります。それぞれのディレクティブが持つ特性と、その背後にある設計思想を理解することで、より効率的で、読みやすい記述ができるようになれればと思っています。今回の記事がVue.jsを使う方の参考になれば幸いです。