Vue 2 | 表示データの変更とイベント処理
Vue.jsに表示されたデータをイベントを通じて更新する。
- Vue 2.5.4
DOMを直接変更しなくてもよい
データが変更されたときにDOMも変更する必要があった処理を、Vue.jsが自動で行ってくれる。これはVue.jsのようなデータバインディングライブラリの大きな特徴であり、表示ミスや表現上のバグを減らせる。多くの開発者がJavaScriptフレームワークを使う大きな理由も、この機能が便利だからである。
仮想DOMについて
Vue.jsは最初に、アプリケーションで使用するテンプレートを分解し、実際のDOMに対応するツリー構造の仮想DOMを作る。ツリーを構成する各要素はVNodeと呼ばれる。データが変更されてもすぐにDOMを変更せず、仮想DOMを通じた同期処理の結果を非同期で実DOMに反映するため、不要なDOM操作がなく高速である。
DOM変更は非同期なので、データを操作した直後に同期的にDOMへアクセスしても、まだ変更前の状態のまま残っていることがある。
イベント処理を登録する
「要素にマウスが乗ったとき」「ボタンをクリックしたとき」などのイベントを扱うには、v-onディレクティブを使う。jQueryのonに似ている。
<button v-on:click="処理内容またはメソッド名">click!</button>
v-onは@に省略できる。v-bindと同じく、最初はv-onで書き、慣れてから省略することをおすすめする。
<button @click="処理内容またはメソッド名">click!</button>
修飾子を使えるようになると、preventやselfのような判定も可能になる。
<div @click.self="処理内容またはメソッド名" class="overlay">...</div>
簡単なテキストを変更する
簡単なテキストを変更してみよう。次の例では、ボタンをクリックするとmethodsに定義されたtestメソッドを呼び出す。処理内容は、dataに定義されているcountプロパティの値を増やすことである。
<div id="app" class="area">
<p>あなたは{{ count }}回クリックしました。</p>
<button v-on:click="test">click!</button>
</div>
DOMを変更する処理はない。
new Vue({
el: '#app',
data: {
count: 0
},
methods: {
test: function() {
this.count++
}
}
})
this.count++でcount値を増やすと、表示部分も一緒に更新される。
v-onもディレクティブなので、値に式を使用できる。繰り返し使わない短いコードなら、メソッドなしで式を直接書ける。ただしテンプレートにコードを入れると見通しが悪くなるため、基本的にはメソッドを指定するのがおすすめである。
<button @click="count++">click!</button>
v-modelを使う
v-modelはフォーム入力とデータを結び付けるディレクティブで、接続されたフォームの入力値や選択値とデータを常に同期できる。
<div id="app">
<p>あなたは「{{ message }}」と入力しました。</p>
<p><input type="text" v-model="inputMessage" /><button @click="getMessage">click!</button></p>
</div>
new Vue({
el: '#app',
data: {
message: '',
inputMessage: ''
},
methods: {
getMessage: function() {
this.message = this.inputMessage
}
}
})
データと同期しているため、messageを直接結び付けると、上の例のようにクリックしなくても入力した瞬間にmessageが変更される。
$refsを使ってみる
$refsを使うとDOMを直接参照できる。後で学ぶコンポーネントタグにも使用できる。注意点は、これは仮想DOMを使わないためレンダリング最適化が行われないことである。何かを操作するたびにDOMへアクセスして再描画するため、描画更新には向いていない。基本的に多用はしないが、要素を参照したい場合もあるので覚えておこう。
テンプレートのタグにrefで名前を付けておくと、this.$refs.NAMEで参照できる。
<div id="app">
<p><input type="text" ref="message"> <button @click="getMessage">click!</button></p>
</div>
new Vue({
el: '#app',
methods: {
getMessage: function() {
alert(this.$refs.message.value)
}
}
})
要素の位置や高さなど、直接DOMへアクセスする必要がある場合に使う。
リストデータの更新
配列の更新
用意された英単語リストのうち、インデックス0のappleを大文字に変更してみる。
new Vue({
el: '#app4',
data: {
list: ['apple', 'banana', 'strawberry']
},
methods: {
test: function() {
this.list[0] = this.list[0].toUpperCase()
}
}
})
このように書いても動作しない。JavaScriptの制限により配列を直接変更してもVueが検知できないため、$setメソッドを使用する。
vm.$set(object, key, value)
このデータ置換は次のように書く。
this.$set(this.list, 0, this.list[0].toUpperCase())
今回は正しく変更された。この仕様はIE対応の影響が大きい。将来のVue.jsではIEサポートがなくなり、Vue.setが不要になるかもしれない。
オブジェクトの変更
オブジェクトは問題なく変更できる。
new Vue({
el: '#app',
data: {
list: {
a: 'apple',
b: 'banana',
c: 'strawberry'
}
},
methods: {
test: function() {
this.list.a = this.list.a.toUpperCase()
}
}
})
ただし、もともと存在しないプロパティを追加する場合はthis.$setが必要である。
オブジェクトリストの変更
配列でも、インデックスを直接置き換えなければ動作するようである。
<div id="app">
<ul>
<li v-for="(val, idx) in list" v-bind:key="val.name">
(No:{{ idx }}) {{ val.name }} {{ val.count }}個
</li>
</ul>
<button v-on:click="test(100, $event)">click!</button>
</div>
v-onハンドラは、引数を持つインライン形式でも実行できる。この場合、$eventで元のイベントオブジェクトを渡せる。
new Vue({
el: '#app',
data: {
list: [{
name: 'リンゴ',
count: 0
}, {
name: 'バナナ',
count: 0
}, {
name: 'イチゴ',
count: 0
}]
},
methods: {
test: function(count) {
this.list[0].count = count
}
}
})
正常に動作した。配列のインデックスを直接触らなければよいようで、内部のオブジェクト監視は動作しているようだ。
リストデータをv-modelで変更する
v-forで得たリストのデータを直接v-modelに指定するのは避けるべきだが、元が配列データであれば接続できる。
<div id="app">
<ul>
<li v-for="(val, idx) in list" v-bind:key="idx">
(No:{{ idx }}) {{ val }} <input size="10" v-model="list[idx]">
</li>
</ul>
</div>
jQueryのような外部変更に対応する
最初はjQueryのようなプラグインを一緒に使いたいこともある。その場合はJavaScriptのdispatchEventを使う。
var eventInput = document.createEvent('UIEvents')
eventInput.initEvent('input', false, false);
$(document).on('click', '[data-update]', function() {
$('#message').val($(this).data('update'))
$('#message')[0].dispatchEvent(eventInput)
});
参照だけなら問題は少ないが、テーブルソートのように要素を再構成する処理はなるべく避けたほうがよい。実DOMを操作してもデータが更新されるわけではなく、場合によっては仮想DOMによって修正されるためである。Vue.jsでは主役はDOMではなくデータである。リストの順序変更やフィルタリングは、次章で紹介する算出プロパティを使うと簡単にできる。
データ更新で忘れがちな点
オブジェクトへ順番に追加する場合でも、配列インデックスを使って更新する場合は$setを使わないと反応しない。必要な場合以外は、最後にオブジェクトリストを更新する形にすると楽かもしれない。
結論
データと要素を連動させるのはjQueryで書くよりかなり便利だが、コードが大きくなるとデータの整合性を保つのが難しくなる。その補完として登場したのがVuexなのだろう。
今回はv-onによるイベント登録方法とデータ更新の基本を学んだ。次は条件分岐による操作を見ていく。