よこのじ(@yokonoji_work)です。
JavaScriptのフレームワーク Vue.js を学んだので、その基礎知識をまとめておきます。Vue.jsの基礎的な使い方を書いていますので、忘れたりなんかした場合にご活用ください。
Vue.jsの公式サイトにはとても丁寧なガイドがあるので、そちらも要チェックです。
- Vue.js のインストール
- Vue.js の書き方・使い方
- テキストのデータバインディング
- HTMLの属性のデータバインディング v-bind
- 条件分岐の v-if
- 表示/非表示の切り替え v-show
- 繰り返し描画 v-for
- イベント処理 v-on
- 双方向データバインディング v-model
- コンポーネント Vue.component
- V-once ディレクティブ
- v-pre ディレクティブ
- v-html ディレクティブ
- v-cloak ディレクティブ
- v-text ディレクティブ
- マスタッシュ構文で式を書く
- ローカルフィルタ
- グローバルフィルタ
- 複数のフィルタを使う
- フィルタに引数を渡す
- 算出プロパティ
- 算出プロパティとメソッドの違い
- 監視プロパティ(ウォッチャ)
- 算出プロパティと監視プロパティの違い
- 監視プロパティの deep オプション
- 監視プロパティの immediate オプション
- クラスのデータバインディング
- 複数のクラスを切り替える
- 純粋なclass記述との共存
- 配列で複数のクラスをv-bindで渡す
- オブジェクトを使ってv-bindにクラスを渡す
- 三項演算子でクラスを切り替える
- 条件 v-if と v-else-if と v-else
- v-show ディレクティブ
- v-on のインラインメソッドハンドラ
- コンポーネントに処理を持たせる
- イベント $event
- v-onで使えるイベント修飾子
- キー修飾子 keyup
- システム修飾子
- 複数行のテキスト
- チェックボックスのデータバインディング
- ラジオボタンのデータバインディング
- セレクトボックスのデータバインディング
- v-model の修飾子
- トランジション(transition)
- axios を利用した API の使用
- ToDoアプリ
Vue.js のインストール
Vue.js は、CDNを使って簡単に組み込むことができます。
開発バージョン
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
本番バージョン
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js"></script>
本番バージョンはミニファイしたものなので min と付けます。これにより読み込みが早くなります。
また、開発バージョンには警告の出力などのデバッグ機能がありますが、本番環境にはないという違いがあります。
Vue.js の書き方・使い方
テキストのデータバインディング
作成したインスタンスで要素を el: で指定して、HTML側のidと対応させます。
その上で、二重中括弧(マスタッシュ Mustache)構文を用いてデータを表示させます。
【HTML】 <div id="app"> <p>{{message}}</p> // Hello Vue.js! <p>{{count}}</p> // 0 <p>{{user.prefecture}}</p> // Tokyo <p>{{colors[1]}}</p> // Green </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js!', count: 0, user: { lastName: 'Yamada', firstName: 'Taro', prefecture: 'Tokyo' }, colors: ['Red', 'Green', 'Blue'] } })
HTMLの属性のデータバインディング v-bind
この例では、v-bind:value=”message で input 要素の value 属性を message の内容にバインディングしている。これにより、入力欄の中に Hello Vue.js と入力された状態となる。
【HTML】 <div id="app"> <input type="text" v-bind:value="message"> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js' } })
v-bind:value=”message” の部分は省略して記述することもできます。
<input type="text" :value="message">
条件分岐の v-if
この例では、toggle がtrueを持つため、pタグが有効となり中身が表示される。falseとした場合はpタグ自体が出力されない。
【HTML】 <div id="app"> <p v-if="toggle"> Hello Vue.js </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { toggle: true } })
表示/非表示の切り替え v-show
v-ifと同様に要素の内容を表示させたり、非表示させたりできる。しかし、v-showはCSSの display プロパティをON/OFFさせて表示/非表示を切り替える点に違いがある。
【HTML】 <div id="app"> <p v-show="toggle"> Hello Vue.js </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { toggle: true // false で非表示 } })
繰り返し描画 v-for
ul, ol, li タグでリスト表示させるときなど、複数のデータを繰り返し描画させるときに利用する。
【HTML】 <div id="app"> <ul> <li v-for="value in user">{{value}}</li> </ul> <hr> <ul> <li v-for="(value, key) in user">{{key}}:{{value}}</li> </ul> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { user: { firstName: 'Ichiro', lastName: 'Suzuki', age: 20 } } })
これを実行すると、次のような結果が得られます。
- Ichiro
- Suzuki
- 20
- firstName:Ichiro
- lastName:Suzuki
- age:20
イベント処理 v-on
この例では、ボタンをクリックしたときに現在時刻を表示できます。
【HTML】 <div id="app"> <button v-on:click="onclick"> Click </button> <p> {{nowTime}} </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { nowTime: '' }, methods: { onclick: function(){ this.nowTime = new Date().toLocaleString(); } } })
なお、v-on:click の部分は @click と省略形で記述することもできます。
イベントにはダブルクリックやキーを押したとき、ホバーしたときなどの指定もできます。
双方向データバインディング v-model
v-model を用いると、入力欄に入力した値にdataオブジェクトが変更され、dataオブジェクトを変更すると入力欄の値が変わる、という双方向の操作が可能になります。
【HTML】 <div id="app"> <p> <input type="text" v-model="message"> </p> <p> <input type="text" v-model="message"> </p> <pre>{{$data}}</pre> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js' } })
v-model はvalue属性を操作するもの。v-model 利用時には input 要素にvalue属性の記述を行っていても無視される点に注意しましょう。
コンポーネント Vue.component
ページを部品に分けて扱うことで、再利用性が高くなり、コードが見やすくなる。
Vue.component(コンポーネント名, コンポーネント定義情報) と記述します。
これにより、<hello-component></hello-component> とHTMLに記述するだけで、コンポーネント内で定義した内容<p>Hello</p>が出力されます。
よって、このように2回Helloが表示されるというわけです。
Hello
Hello
【HTML】 <div id="app"> <hello-component></hello-component> <hello-component></hello-component> </div> ---------------------------------- 【JavaScript】 Vue.component('hello-component', { template: '<p>Hello</p>' }) var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js' } })
コンポーネント名は、ケバブケース(”-“を含むもの)にする必要があります。HTMLの要素名は1単語になっていて、将来追加するものと重複しないためのようです。
例:hello-component
なお、コンポーネントは次のようにローカル登録することもできます。
var helloComponent = { template: '<p>Hello</p>' } var app = new Vue({ el: '#app', components: { 'hello-component': helloComponent } })
V-once ディレクティブ
初回だけテキストバインディングが行われ、以降は静的なコンテンツとなる。
この例では、テキストを反転させるボタンが用意されているが、{{message}} は v-once により初回に「Hello Vue.js」と表示された以降はバインディングされない。
【HTML】 <div id="app"> <p v-once> {{message}} </p> <button v-on:click="clickHandler"> Click </button> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js' }, methods: { clickHandler: function(event){ this.message = this.message.split('').reverse().join('') } } })
v-pre ディレクティブ
要素とすべての子要素のコンパイルをスキップする。
この例では、message の内容を表示するのではなく、マスタッシュ構文 {{message}} がそのまま表示される。
【HTML】 <div id="app"> <p v-pre> {{message}} </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js' } })
ディレクティブのない大量のノードをスキップして、コンパイルのスピード上げるなどの用途がある。
v-html ディレクティブ
テキストをHTMLコードとして挿入できる。
v-htmlを使わない場合は、HTMLコードをただの文字列として表示するが、v-htmlを使うとスタイルなどが適用された状態で表示される。
【HTML】 <div id="app"> <p> {{message}} </p> <p v-html="message"><p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello <span style="color:red">Vue.js</span>' } })
v-html を使用すると、クロスサイトスクリプティング(XSS)脆弱性の問題がある。例えば、悪意のあるリンクをユーザーが勝手にページに表示させるなどの攻撃を行うことができる。
参考:クロスサイトスクリプティング(XSS)のセキュリティ対策とは?
v-cloak ディレクティブ
HTMLでマスタッシュ構文が表示されてから、Vueインスタンスの内容が適用されるまでの間に {{message}} のように生のマスタッシュ構文が一瞬表示されてしまうことがある。
このチラツキを防止するために、v-cloakを使用して、CSSで display: none; を設定しておきます。
こうすると、Vueインスタンスが準備できるまでは対象要素が表示されず、Vueインスタンスが準備できてはじめて要素が表示されるようになります。
【HTML】 <div id="app"> <p v-cloak> {{message}} </p> </div> ---------------------------------- 【CSS】 [v-cloak] { display: none; } ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js' } })
v-text ディレクティブ
テキストバインディングさせるマスタッシュ構文{{message}}と同様の扱いが可能。
【HTML】 <div id="app"> <p v-text="message"></p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js' } })
マスタッシュ構文で式を書く
四則演算やメソッドの適用、条件式などを下記のようにマスタッシュ構文内に書くことができる。
【HTML】 <div id="app"> <p> {{number + 1}} </p> <p> {{message.split('').reverse().join('')}} </p> <p> {{ok ? 'YES' : 'NO'}} </p> <p> {{ok ? message : ''}} </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js', number: 100, ok: true } })
ローカルフィルタ
式 | フィルタ名 のように記述すると、指定したフィルタを当てることができる。
この例では、数値を3桁ごとにカンマ(,)で区切るフィルタを当てている。
【HTML】 <div id="app"> <P> {{price | numberFormat}} </P> <input type="text" v-bind:value="price | numberFormat"> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { price: 1980 }, filters: { numberFormat: function(value){ return value.toLocaleString() } } })
グローバルフィルタ
グローバルなフィルタを定義することもできます。
次のように インスタンスの生成より前に Vue.filter() で定義します。使い方はローカルフィルタと同じです。
【HTML】 <div id="app"> <P> {{price | numberFormat}} </P> <input type="text" v-bind:value="price | numberFormat"> </div> ---------------------------------- 【JavaScript】 Vue.filter('numberFormat', function(value){ return value.toLocaleString() }) var app = new Vue({ el: '#app', data: { price: 1980 }, })
複数のフィルタを使う
フィルタはいくつかを連結して使うことができます。
この例では、jpyPrice に対して、ドルに変換するtoUSDと3桁ごとにカンマを付けるnumberFormatの2つのフィルタを当てています。
【HTML】 <div id="app"> <p> {{jpyPrice | toUSD | numberFormat}} </p> </div> ---------------------------------- 【JavaScript】 Vue.filter('toUSD', function(jpy){ return jpy / 100 }) Vue.filter('numberFormat', function(value){ return value.toLocaleString() }) var app = new Vue({ el: '#app', data: { jpyPrice: 10000000 }, })
フィルタに引数を渡す
フィルタは引数を扱うことも可能です。
この例では、「もっと読む」で使われるような、指定したテキスト以降は…のように省略するフィルタを使っていて、文字数や末尾を指定できるようになっています。
Lorem…
Lorem ipsu>>>
【HTML】 <div id="app"> <p> {{text | readMore(5, '...')}} </p> <p> {{text | readMore(10, '>>>')}} </p> </div> ---------------------------------- 【JavaScript】 Vue.filter('readMore', function(text, length, suffix){ return text.substring(0, length) + suffix }) var app = new Vue({ el: '#app', data: { text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit' } })
算出プロパティ
関数で算出したデータを返すことができます。
テキストを反転させる {{message.split(”).reverse().join(”)}} という記述は長いですし、複数箇所で使う場合は見にくいコードになります。
この例では、インスタンス内で reversedMessage という名前の関数を定義しており、この関数を呼べばテキストを反転できます。これにより簡単に使い回せるようになります。
【HTML】 <div id="app"> <p> {{reversedMessage}} </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js' }, computed: { reversedMessage: function(){ return this.message.split('').reverse().join('') } } })
算出プロパティとメソッドの違い
- 算出プロパティは () が不要
- メソッドは () 必要
【HTML】 <div id="app"> <p> {{ reversedMessage }} </p> <p> {{ reversedMessageMethod() }} </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js' }, computed: { reversedMessage: function(){ return this.message.split('').reverse().join('') } }, methods: { reversedMessageMethod: function(){ return this.message.split('').reverse().join('') } } })
- 算出プロパティは getter, setter が使える
- メソッドは gettet のみ使える
ゲッターはある属性の値を取得するメソッドです。セッターは属性に値を設定するメソッドです。
算出プロパティでgetterとsetterを使う例
【HTML】 <div id="app"> <p> 定価: <input type="text" v-model="basePrice"> </p> <p> 税込: <input type="text" v-model="taxIncludedPrice"> </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { basePrice: 100 }, computed: { taxIncludedPrice: { get: function(){ return parseInt(this.basePrice * 1.08) }, set: function(taxIncludedPrice){ this.basePrice = Math.ceil(taxIncludedPrice / 1.08) } } } })
- 算出プロパティは キャッシュされる
- メソッドは キャッシュされない
この例は、算出プロパティとメソッドで Math.random() を使って乱数を生成しています。それを3回ずつ呼び出しているのですが、算出プロパティはキャッシュが効いているので3回とも同じ値です。
【HTML】 <div id="app"> <h2>算出プロパティ</h2> <ol> <li>{{computedNumber}}</li> <li>{{computedNumber}}</li> <li>{{computedNumber}}</li> </ol> <h2>メソッド</h2> <ol> <li>{{methodsNumber()}}</li> <li>{{methodsNumber()}}</li> <li>{{methodsNumber()}}</li> </ol> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', computed: { computedNumber: function(){ return Math.random() } }, methods: { methodsNumber: function(){ return Math.random() } } })
算出プロパティの値が変わるトリガーとなるのはリアクティブなデータのときですが、Math.random()はリアクティブではないので、このような結果になります。
算出プロパティは、キャッシュにより速い処理が可能な場合がありますが、同時に上記のように乱数の結果が同じという動作をすることも覚えておきましょう。
監視プロパティ(ウォッチャ)
特定のデータ・算出プロパティの状態を監視して、変化があったときに登録しておいた処理を実行できる(入力フォームの値が変わると自動でAjax通信をして結果一覧を表示させるなど)。
この例は、入力フォームで値を変更するとコンソールに元々入力されていた値と変更後の値が表示されます。 new: Hello Vue.js@, old: Hello Vue.js
これは、監視している message データの状態が変わったときに処理が行われることで実現しています。
【HTML】 <div id="app"> <P> {{message}} </P> <p> <input type="text" v-model:value="message"> </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js' }, watch: { message: function(newValue, oldValue){ console.log('new: %s, old: %s', newValue, oldValue) } } })
km, m, mmの値換算。km, m, mm それぞれの入力フォームの値の変化をウォッチして、変化があったときにその値を単位を変えて他の入力フォームに表示させる。
【HTML】 <div id="app"> <p><input type="text" v-model:value="km">km</p> <p><input type="text" v-model:value="m">m</p> <p><input type="text" v-model:value="mm">mm</p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { km: 0, m: 0, mm: 0 }, watch: { km: function(value){ this.km = value this.m = value * 1000 this.mm = value * 1000000 }, m: function(value){ this.km = value / 1000 this.m = value this.mm = value * 1000 }, mm: function(value){ this.km = value / 1000000 this.m = value / 1000 this.mm = value } } })
算出プロパティと監視プロパティの違い
算出プロパティと監視プロパティを比べると、算出プロパティの方がシンプルに書くことができます。
名前と名字を入力すると、スペースで区切ったフルネームを表示させます。
<div id="app"> <p> 名前:<input type="text" v-model:value="firstName"> </p> <p> 名字:<input type="text" v-model:value="lastName"> </p> <p> {{fullName}} </p> </div>
算出プロパティ
var app = new Vue({ el: '#app', data: { firstName: '', lastName: '' }, computed: { fullName: function(){ return this.firstName + ' ' + this.lastName } } })
監視プロパティ
var app = new Vue({ el: '#app', data: { firstName: '', lastName: '', fullName: '' }, watch: { firstName: function(value){ this.fullName = value + ' ' + this.lastName }, lastName: function(value){ this.fullName = this.firstName + ' ' + value } } })
この例では、算出プロパティの方がスッキリとしていることがよく分かります。
監視プロパティの deep オプション
ネスト(入れ子)されたオブジェクトも監視します。
この例ではcolorsをウォッチしています。しかし、deep: true としているため、例えばChromeのコンソールで app.colors[1].name = ‘White’ のようにcolorsの中のnameを書き換えても変更を検知します。
deep: false ではネストの監視は無効になります。なお、デフォルトでは false なので必要なときのみ記述すれば良いです。
【HTML】 <div id="app"> <ul> <li v-for="color in colors"> {{color.name}} </li> </ul> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { colors: [ {name: 'Red'}, {name: 'Green'}, {name: 'Blue'} ] }, watch: { colors: { handler: function(newValue, oldValue){ console.log('Update') console.log('nre: %s, old: %s', JSON.stringify(newValue, null, '\t'), JSON.stringify(oldValue, null, '\t')) }, deep: true } } })
オブジェクト/配列を変更する場合、それらは、同じオブジェクト/配列を参照するため、ハンドラの引数で取得できる古い値は、新しい値と同じになるという仕様になっているため、上の例でnewValueとoldValueは同じ値になる。
監視プロパティの immediate オプション
初期読み込み時にも監視プロパティを呼び出す。
ひとつ上で書いたコードに追加するならば、deep プロパティの後に続けて書きます。
deep: true, immediate: true
これにより、ソースコードの読み込み一発目にも監視プロパティの処理が行われます。
クラスのデータバインディング
データバインディングにより、クラスを動的に扱うことができます。
v-bind:class=”{large:isLarge}” と記述しているので、isLarge: true のときはlatgeクラスが適用されます。しかし、falseのよきはクラスが適用されません。
【HTML】 <div id="app"> <p> Hello <span v-bind:class="{large:isLarge}">Vue.js</span> </p> </div> ---------------------------------- 【CSS】 .large { font-size: 36px; } ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { isLarge: true } })
また、クラスは v-bind:style を使ってインラインでCSSを当てることもできます。
複数のクラスを切り替える
複数のクラスを扱うこともできます。
文字色を赤にするCSSの記述を行い、isRed: true を定義します。v-bind:classでは、カンマで区切って2つ目以降のクラスを記述します。
クラス名に”-“が入っているのでクォーテーションで囲っている点に注意してください。
【HTML】 <div id="app"> <p> Hello <span v-bind:class="{large:isLarge, 'text-red': isRed}">Vue.js</span> </p> </div> ---------------------------------- 【CSS】 .large { font-size: 36px; } .text-red { color: red; } ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { isLarge: true, isRed: true } })
純粋なclass記述との共存
プレーンなHTMLでのクラス指定とは別に v-bind:class を書くことができます。
この例では、class=”text-blue” と v-bind:class=”{‘text-red’: isRed}” の記述があります。Vueインスタンス内でisRed: trueであればRedがで適用されます。
【HTML】 <div id="app"> <p> Hello <span class="text-blue" v-bind:class="{'text-red': isRed}">Vue.js</span> </p> </div> ---------------------------------- 【CSS】 .text-blue { color: blue; } .text-red { color: red; } ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { isRed: true } })
ハマったところ
isRedがtrueのときに要素をみると<span class=”text-blue text-red”>Vue.js</span>になっているのに赤色にならなかった。
いろいろと試していると、CSS内で .text-red を .text-blue より上に記述しているのが原因でした。class=”text-blue text-red”のようにtext-redが後ろにあるのでこちらが優先されると思っていたのだけれど・・・。
とりあえず、vue.jeで操作したいスタイルは後ろに書いておけば良さそうです。
配列で複数のクラスをv-bindで渡す
配列を使って複数のクラスを適用させることもできます。
【HTML】 <div id="app"> Hello <span v-bind:class="[largeClass, redClass]">Vue.js</span> </div> ---------------------------------- 【CSS】 .large { font-size: 36px; } .text-red { color: red; } ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { largeClass: 'large', redClass: 'text-red' } })
オブジェクトを使ってv-bindにクラスを渡す
オブジェクトに複数のクラスを持たせておき、オブジェクトをv-bindに渡す方法もあります。
【HTML】 <div id="app"> Hello <span v-bind:class="classObject">Vue.js</span> </div> ---------------------------------- 【CSSt】 .large { font-size: 36px; } .text-red { color: red; } ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { classObject: { large: true, 'text-red': true } } })
三項演算子でクラスを切り替える
三項演算子により isLarge の状態を見て、trueならlargeClass、falseなら空(何も適用しない)としています。
【HTML】 <div id="app"> <p> Hello <span v-bind:class="isLarge? largeClass : ''">Vue.js</span> </p> </div> ---------------------------------- 【CSSt】 .large { font-size: 36px; } .text-red { color: red; } ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { largeClass: { large: true }, redClass: { 'text-red': true }, isLarge: true } })
v-bind:class=”[ isLarge? largeClass : ”, redClass ]” のように三項演算子を配列内で記述することも可能です。
条件 v-if と v-else-if と v-else
colorの値と一致する条件がある箇所のp要素を挿入します。他の部分はDOMレベルで存在しないものとなります。
【HTML】 <div id="app"> <p v-if="color==='red'">Stop</p> <p v-else-if="color==='yellow'">Caution</p> <p v-else-if="color==='blue'">Go</p> <p v-else>Not red/yellow/blue</p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { color: 'blue' } })
v-show ディレクティブ
条件判定するのはv-ifと同じだが、DOMレベルであり/なしを切り替えるのではなく、CSSのdisplayプロパティで表示/非表示を切り替える。v-if-elseのように複数の条件は指定できない。
【HTML】 <div id="app"> <p v-show="toggle"> Hello Vue.js </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { toggle: true } })
toggleがfalseのときは <p style=”display: none;”>Hello Vue.js</p> のようにdisplay: noneにより非表示になる。
v-on のインラインメソッドハンドラ
ボタンをクリックしたときのようなイベント発生時の処理をインラインで書くことができます。
【HTML】 <div id="app"> <p> {{counter}} </p> <button v-on:click="counter++"> Click </button> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { counter: 0 } })
コンポーネントに処理を持たせる
ボタンを表示して、そのボタンを押すとカウントを+1する機能をVue.componentで作っています。
テンプレートを使って再利用が可能で、各カウントは独立しています。
【HTML】 <div id="app"> <button-counter></button-counter> <button-counter></button-counter> </div> ---------------------------------- 【JavaScript】 Vue.component('button-counter', { data: function(){ return { count: 0 } }, template: '<div><span>カウント:</span><button v-on:click="count++">{{count}}</button></div>' }) var app = new Vue({ el: '#app', })
イベント $event
イベントオブジェクトを参照したい場合は、$eventを明示する必要がある。
【HTML】 <div id="app"> <p> {{message}} </p> <button v-on:click="clickHandler($event, 'Vue.js')"> Click </button> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: '' }, methods: { clickHandler: function($event, message){ this.message = message console.log($event) } } })
v-onで使えるイベント修飾子
- .stop イベントの親要素への伝搬を中止
- .prevent イベントの既定動作をキャンセル
- .capture イベントハンドラをキャプチャモードで動作
- .self イベントの発生元が要素実親の場合にのみ実行
- .once イベントハンドラを一回だけ実行
- .passive passiveモードを有効化
Vueの公式ガイドも参考に。
キー修飾子 keyup
マウス操作ではなく、キーボード操作も v-on に指定できます。
この例では、ESCキーかスペースキーか上キーを押すと入力フォームの値が空になります。
【HTML】 <div id="app"> <input type="text" v-on:keyup.esc.space.up="clear" v-model="message"> <p> {{message}} </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: '' }, methods: { clear: function(){ this.message = '' } } })
エンターキーやタブキーも指定できると公式ガイドに書かれています。
- .enter
- .tab
- .delete (Backspace キーでも効く)
- .esc
- .space
- .up
- .down
- .left
- .right
その他、数字やローマ字、ファンクションキーなどを使いたい場合はキーコード一覧を参考にしてください。
システム修飾子
システム修飾子を使うと、これらのキーが押されているときにイベントをトリガーすることができます。
- .ctrl
- .alt
- .shift
- .meta (macのコマンドキーやWindowsマークのキー)
上のフォームの値を空にする例でシステム修飾子を使うと、次のように記述できます。これはCtrlとCを押したときに空にする記述です。
<input type="text" v-on:keyup.ctrl.67="clear" v-model="message">
複数行のテキスト
複数行のテキスト入力フォーム <textarea> ではマスタッシュ構文は効かない。<textarea>{{message}}</textarea> のように書いてもデータバインディングされないのです。
しかし、v-modelを使ってtextarea内に値を表示させた場合は、複数行テキストでもデータバインディングできます。
【HTML】 <div id="app"> <pre>{{message}}</pre> <textarea v-model="message">{{message}}</textarea> <pre>{{$data}}</pre> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { message: 'Vue' } })
チェックボックスのデータバインディング
v-model を使ってチェックボックスの状態を双方データバインディングすることができます。
【HTML】 <div id="app"> <input type="checkbox" id="checkbox" v-model="checked"> <label for="checkbox">{{checked}}</label> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { checked: false } })
複数のチェックボックスを扱うときは、配列として状態が保管されます。公式ガイドを参考にしてください。
ラジオボタンのデータバインディング
v-model を使ってラジオボタンのデータバインディングをすることができます。
【HTML】 <div id="app"> <input type="radio" id="red" value="Red" v-model="color"> <label for="red">Red</label> <input type="radio" id="green" value="Green" v-model="color"> <label for="green">Green</label> <p>{{color}}</p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { color: '' } })
セレクトボックスのデータバインディング
v-model を使ってセレクトボックスのデータバインディングをすることができます。
【HTML】 <div id="app"> <p> {{selected}} </p> <select v-model="selected"> <option disabled value="">選択してください</option> <option>Red</option> <option>Green</option> <option>Blue</option> </select> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { selected: '' } })
複数行表示のセレクトボックスの場合は、配列に状態が保管されます。公式ガイドを参考にしてください。
v-model の修飾子
v-model には修飾子を当てることができます。
- .lazy バインドのタイミングを遅延させる。入力フォームの場合、カーソルが外れたタイミングで確定する。
- .trim 入力値から前後の空白を削除
- .number 入力値を数値型に型変換
input で type=”number” にしていても、返ってくる値は文字列なので、+10する計算をしたとしても1010のように文字列として連結されるだけになる。そこで、.number 修飾子を用いると数値として扱われるようになる。
【HTML】 <div id="app"> <input type="number" v-model.number="num"> <p> {{num+10}} </p> </div> ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { num: 0 } })
トランジション(transition)
トランジション(transition)を利用して、DOMから要素が追加・削除されるときに視覚効果を付けることができます。
【HTML】 <div id="app"> <button v-on:click="show=!show"> Transition </button> <transition> <p v-show="show"> Hello Vue.js </p> </transition> </div> ---------------------------------- 【CSS】 .v-enter-active, .v-leave-active { transition: opacity 2s; } .v-enter, .v-leave-to { opacity: 0; } ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { show: false } })
もっとダイナミックに動かすこともできるので、公式ガイドをチェックしてください。
axios を利用した API の使用
公式ガイドの「axios を利用した API の使用」でも紹介されている、axiosを利用してapiを叩きます。
【HTML】 <div id="app"> <h2> Bitcoin Price </h2> <section v-if="hasError"> Error </section> <section v-else> <div v-if="loading"> Loading... </div> <div> <ul v-cloak> <li v-for="(rate, currency) in bpi"> {{currency}} : {{rate.rate_float | currencyDecimal}} </li> </ul> </div> </section> </div> ---------------------------------- 【JavaScript】 <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> <!-- axios --> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> var app = new Vue({ el: '#app', data: { bpi: null, hasError: false, loading: true }, mounted: function(){ axios.get('https://api.coindesk.com/v1/bpi/currentprice.json').then(function(response){ this.bpi = response.data.bpi }.bind(this)) .catch(function(error){ console.log(error) this.hasError = true }.bind(this)) .finally(function(){ this.loading = false }.bind(this)) }, filters: { currencyDecimal(value) { return value.toFixed(2) } } })
ToDoアプリ
【HTML】 <div id="app"> <h2>TODO List</h2> <form v-on:submit.prevent> <input type="text" v-model="newItem"> <button v-on:click="addItem"> Add </button> </form> <ul> <li v-for="(todo, index) in todos"> <input type="checkbox" v-model="todo.isDone"> <span v-bind:class="{done: todo.isDone}">{{todo.item}}</span> <button v-on:click="deleteItem(index)"> Delete </button> </li> </ul> <!-- <pre>{{$data}}</pre> --> </div> ---------------------------------- 【CSS】 #app ul { list-style: none; } #app li > span.done { text-decoration: line-through; } ---------------------------------- 【JavaScript】 var app = new Vue({ el: '#app', data: { newItem: '', todos: [] }, methods: { addItem: function(event){ if(this.newItem==='') return; var todo = { item: this.newItem, isDone: false }; this.todos.push(todo); this.newItem = ''; }, deleteItem: function(index){ this.todos.splice(index, 1) } } })