【第2回】Vue.js 入門 〜ディレクティブ編〜
IT技術
ディレクティブを使ってみよう!
(株) ライトコードの宮田です。
前回は、Vueインスタンスを作成し、data に持たせた "Hello World" の文字列を表示するプログラムを作成しました。
今回は「ディレクティブ」と呼ばれる、Vue.jsが提供している特別な属性を利用したプログラムを書いてみましょう!
前回の記事はこちら
ディレクティブとは
接頭辞 v- が付いたVue.jsの特別な属性のことを指します。
その式の値が変更されるとき、 DOM に対してリアクティブに特殊な動作を適用することができます。
準備
まずは、前回と同様にプロジェクトファイルと index.htmlファイルを用意しましょう。
1$ mkdir vue-directive
2$ cd vue-directive
3$ touch index.html
index.htmlの内容は、 Vue.js の CDN を読み込むところまでで、下記のように記述しておきます。
1<!DOCTYPE html>
2<html>
3 <head>
4 <meta charset="utf-8">
5 <title>ディレクティブの使い方</title>
6 </head>
7 <body>
8 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
9 <script>
10 </script>
11 </body>
12</html>
それでは順番にみていきましょう!
v-bind
v-bindディレクティブを利用すると、要素の属性を束縛(バインディング)することができます。
ですが、これだけ聞いてもピンとこないかもしれません。
前回、{{ }} で囲う マスタッシュ(Mustache)構文 を使用したテキスト展開を学習しました。
これは、Vue.jsの データバインディング構文 の最も基本的な形式です。
下記のように、開始タグと終了タグの間の位置に記述しましたね。
1<div id="app">{{ message }}</div>
また、HTML属性の内部で {{ }} を利用したいこともあると思います。
例えば下記のように href 属性に url というプロパティの値を展開したい場合を考えます。
1<a href="{{ url }}"></a> <!--- 失敗例 --->
ですが、残念ながらこの書き方では使用することができません。
そんな時に登場するのが v-bind です!
v-bind を使って書き換えると、下記のようになります。
属性名の前に v-bind: が付き、値は {{ }} で囲まず、プロパティ名をそのまま記述します。
1<a v-bind:href="url">リンク</a>
使用例
1<body>
2 <div id="app-1">
3 <a v-bind:href="url">Google</a>
4 </div>
5 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
6 <script>
7 const app1 = new Vue ({
8 el: '#app-1',
9 data: {
10 url: 'https://www.google.com/'
11 }
12 })
13 </script>
14</body>
index.htmlの <body> 内を上記のコードになるようにして実行した結果が下の画像です。
url の値がちゃんと展開されていますね。
省略記法
v-bind は、下記の例のように省略して記述することもできます。
例) v-bind:href → :href
1<a :href="url">リンク</a>
v-model
主に、 form の input 要素や textarea 要素、select 要素で使用されます。
v-modelディレクティブを利用すると、入力とアプリケーションの状態の 双方向データバインディング を簡単に行うことができます!
言葉で説明するよりも実際に動かしてみてみましょう。
使用例
1<body>
2 <div id="app-2">
3 <p>{{ inputText }}</p>
4 <input v-model="inputText">
5 </div>
6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
7 <script>
8 const app2 = new Vue ({
9 el: '#app-2',
10 data: {
11 inputText: ''
12 }
13 })
14 </script>
15</body>
上記のコードを実際に動かしてみると、テキストボックスで入力した値が上部にテキストとして即座に反映されていますね。
これはテキストボックスの値(value属性の値)が変更されると、 data で持っている inputText の値に自動的に反映される仕組みをv-modelディレクティブが備えているからなんです。
注意点
よくある例で、value や checked、selected 属性に初期値を設定しておきたい場合があると思います。
しかし、v-model には 上記にある属性の初期値を無視するという特徴があります。
例えば、下記のように属性に初期値を記述する方法では失敗します。
1<input v-model="inputText" :value="初期値"> <!--- 失敗例 --->
そのため、下記のコードのように data に初期値を持たせておくか、カスタムディレクティブで独自に実装を追加する必要があります。
1<script>
2 const app2 = new Vue ({
3 el: '#app-2',
4 data: {
5 inputText: '初期値'
6 }
7 })
8</script>
補足
v-model は 下記の内容の 糖衣構文(syntax suger) です。
1<input
2 v-bind:value="inputText"
3 v-on:input="inputText = $event.target.value"
4 >
v-on
v-onディレクティブを利用してイベントリスナーを加えることで、Vue インスタンスのメソッドを呼び出すことができます。
下記の場合、イベントリスナーは click なので、ボタンをクリックした時 counter メソッドが呼び出されるということですね。
1<button v-on:click="counter">クリック</button>
使用例
1<body>
2 <div id="app-3">
3 <p>{{ count }} 回クリックされました!</p>
4 <button v-on:click="counter">押してね</button>
5 </div>
6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
7 <script>
8 const app3 = new Vue ({
9 el: '#app-3',
10 data: {
11 count: 0
12 },
13 methods: {
14 counter: function(){
15 this.count++
16 }
17 }
18 })
19 </script>
20</body>
上記コードを実行してみると、クリック回数のテキストとボタンが表示されたと思います。
コードを見ると、Vueインスタンス内に、新たに methods いうプロパティが登場しています。
まだ今回は詳しく書きませんが、methods には 呼び出したいメソッドを定義することができます。
counter メソッドは data ではなく、この methods に 記述しましょう。
methods から data の中身を参照する際には this が必要なので忘れないようにしましょう!
ボタンを押すごとに counter メソッドが呼びだされて上部のテキストの回数が加算されていますね!
省略記法
v-on は、下記の例のように省略して記述することもできます。
例) v-on:click → @click
1<button @click="counter">クリック</button>
v-ifディレクティブ
v-if ディレクティブを利用すると、条件分岐によって、簡単に要素の有無の切り替えをすることができます。
もちろん、if文による条件分岐なので、 v-if に対応する 「v-else-if」「v-else」ディレクティブもあります!
使用例
例として、会員の種類別にユーザーへ出すメッセージを分けたい場合を考えてみます。
種類は「プレミアム会員」「一般会員」「非会員」の3種類とします。
会員判定用にisMember 、ランク判定用に isPremier というプロパティを用意しました。
会員ではないのにランク判定の値が、 true のパターンは例外として、「不正なユーザーです。」と表示します。
1<body>
2 <div id="app-4">
3 <h2>会員ランク</h2>
4 <p v-if="isMember && isPremier">あなたはプレミアム会員です。</p>
5 <p v-else-if="isMember && !isPremier">あなたは一般会員です。</p>
6 <p v-else-if="!isPremier">あなたは会員ではありません。</p>
7 <p v-else>不正なユーザーです。</p>
8 </div>
9 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
10 <script>
11 const app4 = new Vue ({
12 el: '#app-4',
13 data: {
14 isMember: true,
15 isPremier: false,
16 }
17 })
18 </script>
19</body>
確認
上記コードを実行すると、下の画像のように一般会員用のメッセージが表示されました!
また、isMember 、isPremier の値を変えて、表示の変更を確認してみてください。
注意点
- v-else-if・v-elseディレクティブは、v-if または、v-else-if 要素の直後に記述する必要があります。
v-showディレクティブ
v-showディレクティブを利用すると、値の真偽値によって、要素の表示の切り替えをすることができます。
先ほどの、v-if と機能的には同じように思えますね。
ですが、この2つのディレクティブには、非表示の仕組みに違いがあります。
v-if と v-show の違い
まず、v-if での非表示の場合、 DOM上から削除されるために表示されない状態です。
対して、v-show での非表示は、style属性の display: none; を追加することによって表示と非表示を切り替えているだけなので、DOM上には常に存在している状態です。
実際に、どちらも非表示にして、ディベロッパーツールで確認してみましょう!
- 表示判定用に isVisible というプロパティを用意
- 値を false に設定
1<body>
2 <div id="app-5">
3 <!-- TODO: ここに非表示にするエレメントを記述 -->
4 </div>
5 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
6 <script>
7 const app5 = new Vue ({
8 el: '#app-5',
9 data: {
10 isVisible: false
11 }
12 })
13 </script>
14</body>
v-if
1<p v-if="isVisible">見えていますか?</p>
実行時のディベロッパーツールの表示は、こちらです。
p要素のエレメントがDOM上から消えていることが確認できますね。
v-show
1<p v-show="isVisible">見えていますか?</p>
実行時のディベロッパーツールの表示はこちらです。
p要素に style 属性が追加され、isVisible プロパティの値によって、非表示になっていることがわかります。
エレメントは、常時描画されており、DOM上に保持されていますね。
非表示時の仕組みが違うだけで、実際に画面の表示上は両方とも変わらないためどちらを使ってもいいように思いませんか?
しかし、v-showには DOM上に存在するからこそのメリットがあるんです!
使用例
例えば、表示を頻繁に切り替える必要のあるエレメントを考えてみましょう。
v-if の場合、表示・非表示を切り替えるたびにエレメントの追加・削除を行う処理が生じます。
その結果、表示にかかるコストが高くなってしまうのです。
対して v-show なら style 属性の追加・削除だけで済むため、結果的に切り替えの際の表示コストが低いです。
- 表示の切り替え頻度が多い場合は、v-show
- 表示の切り替え頻度が少ない場合は、v-if
というように、うまく使い分けるようにしましょう!
注意点
v-if と違って、v-show は「v-else-if」や「v-else」とは対応していません。
そのため、else を使って要素を出し分けたい場合などは、 v-if を使用しましょう。
v-for
v-forディレクティブを利用すると、配列に基づいた要素のリストレンダリングをすることができます。
使用するときは、 変数名 in 配列名 という形式の構文で記述します。
また、配列の index を利用したい場合は、 (変数名, index) in 配列名 と記述することで任意の2つ目の引数として利用することができます。
もちろん、配列だけでなく、オブジェクトに対しても使用できます。
使用例
foods という配列のプロパティの中身を展開して、リストとして表示してみたいと思います。
配列内には、それぞれ id と name というキーを持ったオブジェクトが入ります。
変数名は、food とすることにして、記法に則って food in foods としましょう。
v-for を指定しているのと同じエレメントに :key 属性(v-bind:key)も指定してありますね。
key は、 Vue がデータを追跡するために必要で、パフォーマンスの向上にもつながります。
key の値は一意(ユニーク)である必要があるため、今回は食べ物オブジェクトの id を key として設定しました。
1<li v-for="food in foods" :key="food.id">
下記コードを実行してみましょう!
1<body>
2 <div id="app-6">
3 <h2>食べたいものリスト</h2>
4 <ul>
5 <li v-for="food in foods" :key="food.id">
6 {{ food.name }}
7 </li>
8 </ul>
9 </div>
10 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
11 <script>
12 const app6 = new Vue ({
13 el: '#app-6',
14 data: {
15 foods: [
16 { id: 1, name: 'ハンバーグ' },
17 { id: 2, name: '焼肉' },
18 { id: 3, name: 'ステーキ' },
19 { id: 4, name: 'すき焼き' }
20 ]
21 }
22 })
23 </script>
24</body>
確認
foods 配列の中の name の値が順番にリスト形式で表示されました!
注意点 その1
v-for を使用する場合は、v-bindでの key プロパティの指定が必須とされています。
ちなみに、key を指定しない場合、配列の index が key の値に使用されます。
しかし、基本的には、配列の index は key として使用すべきではないようです。
理由は、配列内の要素の追加・削除や順序の変更が行われるとき、indexの値も変化してしまうためです。
先ほど、少し書きましたが、key の値は一意であり、key の値を元に Vue はデータを追跡します。
index の値を key に設定していると、配列内の要素の追加・削除や順序の変更によって key が振り直されてしまいます。
その結果、Vue はデータの変更を追跡できなくなってしまうため、予期せぬ動作を引き起こす可能性があります。
key には、一意かつ、変更が生じない値を設定するようにしましょう!
注意点 その2
v-for と v-if を同じ要素に使用するのは非推奨とされています。
仮に同じ要素で使用した場合は、優先度は v-for の方が高いため先に評価されます。
もし、v-for でレンダリングしている全ての要素に v-if で処理を行いたい場合は、v-for を設定している要素の親要素に v-if を追加することで解決します。
1<ul v-if="isVisible">
2 <li v-for="food in foods" :key="food.id">
3 {{ food.name }}
4 </li>
5</ul>
また、要素をフィルタリングして v-if で出し分けたいという場合は、Computed プロパティを利用して filter関数を定義するという形で解決することができます。
あくまで一例ですが、以下のような感じです。
1<ul>
2 <li v-for="food in visibleFoods" :key="food.id">
3 {{ food.name }}
4 </li>
5</ul>
1computed: {
2 visibleFoods: function () {
3 return this.foods.filter(function (food) {
4 return food.isVisible
5 })
6 }
7}
v-once
v-onceディレクティブを利用すると、Vue によるレンダリングを一度だけ行うようにすることができます。
そのため、初回表示以降に値の変更があっても表示は変更されなくなります。
使用例
分かりやすいように前回の v-onディレクトリの例で使用したカウンターのプログラムを書き換えてみましょう!
下記のように、カウント回数を表示しているp要素に v-once を追加しました。
1<p v-once>{{ count }} 回クリックされました!</p>
すると<body> 内は以下のようなコードになりました。
実行してみましょう!
1<body>
2 <div id="app-7">
3 <p v-once>{{ count }} 回クリックされました!</p>
4 <button v-on:click="counter">押してね</button>
5 </div>
6 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
7 <script>
8 const app7 = new Vue ({
9 el: '#app-7',
10 data: {
11 count: 0
12 },
13 methods: {
14 counter: function(){
15 this.count++
16 }
17 }
18 })
19 </script>
20</body>
確認
ボタンを何度押しても 「0 回クリックされました!」 のまま表示が変わらなくなっていますね。
第3回につづく!
第2回は前編・後編に分けて、Vue.js の主要なディレクトリと使い方についてみてきました。
なんだか少しずつですが Vue が書けるような気がしてきたのではないでしょうか?
次回からは、Vueインスタンスのオプションについてみていきたいと思います!
次回の記事はこちら
2019.12.05【第3回】Vue.js 入門 〜オプション編〜オプションを覚えよう!宮田(株) ライトコード Webエンジニアの宮田です。前回までで、Vue.jsの主要なディレクテ...
こちらの記事もオススメ!
2020.08.07JavaScript 特集知識編JavaScriptを使ってできることをわかりやすく解説!JavaScriptの歴史【紆余曲折を経たプログラミン...
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit