Vue.js を1から学んでみた。にて書いてきましたが、
長くなり編集しにくくなってきたので、分割しました(こんなやりかたしないのかな?知りたい。)
4.ディレクティブ
特徴としては、以下かと。
-
v-
から始まる特別な属性 - 属性値は、単一の JavaScript 式を期待する(例外は
v-for
) - 属性値の式が変更された時に、DOMを更新する
- 省略記法あり。
- 例:
<a v-bind:href="url"> ... </a>
→<a :href="url"> ... </a>
- 例:
v-
のディレクティブについて捉えることが必要そうなので、まとめようと思います。
※次の記事にもまとまっており、参考にもさせていただきました。
体で覚えるVue.js - ディレクティブ編 〜 JSおくのほそ道 #023
v-text
- DOMの内側に展開。
- Mustache構文({{}}で囲んだコード)と一緒。
- 省略系→{{}}
サンプルコード
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-1">
<!-- フィールド展開の例 -->
<p v-text="message"></p>
<p>{{ msg }}</p>
<!-- メソッド展開の例 -->
<p v-text="showMessage()"></p>
<p>{{ showMsg() }}</p>
</div>
var vm = new Vue({
el: '#app-1',
data: {
message: 'Hello Vue.js!',
msg: 'サンキュー'
},
methods: {
showMessage: function () {
return "はろー、Vue.js"
},
showMsg: function () {
return "Thank you!!"
}
}
})
v-once
- 1度DOM展開したら、そのあとは変更させない。
- なんかの処理で、バインディングしたデータが変更されても、描画を変更させたくないときに有効。
- 個人的にはこれを使うのは最終手段かなと。感覚的に。
- 省略系→なし
サンプル(v-onceを使わない)
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-2">
<p v-text="message"></p>
<p v-text="showMessage()"></p>
</div>
var vm = new Vue({
el: '#app-2',
data: {
message: 'Hello Vue.js!'
},
methods: {
showMessage: function () {
this.message = 'hoge';
return "はろー、Vue.js"
}
}
})
結果(「Hello Vue.js!」がshowMessage()で書き換えられて表示される)
サンプル(v-onceを使う・JavaScriptは変更しないので割愛)
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-2">
<!-- v-onceを追加 -->
<p v-once v-text="message"></p>
<p v-text="showMessage()"></p>
</div>
v-html
- DOMの内側に展開。
- v-textは「文字列」で展開する
- v-htmlは「HTML」で展開する
- HTMLをそのまま展開されるので、XSSに注意して使うことが必要。
- 省略系→なし
サンプルコード:h1タグを表示させたいときー
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-3">
<p v-text="message"></p>
<p v-html="message"></p>
</div>
var vm = new Vue({
el: '#app-3',
data: {
message: '<h1>タイトルです<h1>'
}
})
v-bind(その1:属性値)
- htmlタグの属性に埋め込む時に使用。
- 引数が必要なディレクティブ。引数は:の後ろに設定する
v-bind:href="url
- 使う頻度がスーパー高そう。
- 省略系→
:href='url'
のように、コロンだけでOK。
サンプルコード:aタグのリンク先を設定したいときー
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-4">
<!-- :(コロン)の後に引数を設定する-->
<a v-bind:href="url">モノタス</a>
<!-- 省略系
<a :href="url">モノタス</a>
-->
</div>
var vm = new Vue({
el: '#app-4',
data: {
url: 'https://www.monotas.net/'
}
})
v-bind(その2:属性)
- htmlタグそのものを定義することも可能
- 属性を[]で囲うことで指定可能。例:
<a :[attr]="hogehoge">
- つまり、v-bindだけで、各種タグに属性とそのvalueのバインディングができる。
- これも使う頻度がスーパー高そう。
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-5">
<a :href="monotas_url">モノタス</a>
<br>
<a :[attr]="jta_url">日本テニス協会</a>
</div>
var vm = new Vue({
el: '#app-5',
data: {
attr: 'href',
monotas_url: 'https://www.monotas.net/',
jta_url: 'https://www.jta-tennis.or.jp/'
}
})
v-bind(その3:オブジェクト化)
- 1つのタグに複数のv-bindを指定することもできるが、一つのオブジェクトとして記載することも可能。
- Vueフィールドにオブジェクトを用意して、それを
v-bind
で指定することも可能。 - 見た目すっきり系でもあるし、DOMの操作しやすくなりそうな感覚。まだふんわりとしかわかってない。
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-6">
<!-- 複数指定できる -->
<a :id="monotas_id" :href="monotas_url">モノタス(省略系でそれぞれ指定)</a>
<br>
<!-- 一つのオブジェクトでも書ける -->
<a v-bind="{id: monotas_id, href: monotas_url}">モノタス(オブジェクトを直接指定)</a>
<br>
<!-- VueインスタンスフィールドのオブジェクトもOK -->
<a v-bind="monotas">モノタス(Vueインスタンスのオブジェクトを指定)</a>
<br>
</div>
var vm = new Vue({
el: '#app-6',
data: {
attr: 'href',
monotas_id: 2,
monotas_url: 'https://www.monotas.net/',
monotas: {
id: 2,
href: 'https://www.monotas.net/'
}
}
})
v-on(その1:基本)
- クリック等のDOMが提供しているイベントのリスナー。
- イベント発火時のJavaScriptの実行が可能。
- 引数が必要なディレクティブ。引数は: の後ろに設定する
v-on:click="change"
-
click
はDOMが提供しているイベント -
change
が Vueで定義したメソッド。
-
- 使う頻度は超スーパー高そう。
- 省略系→
@click="change"
のように、コロンだけでOK。
サンプルコード:clickイベントの例。ボタンが押されたら文字列を変更します的なときー
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-7">
<button v-on:click="change">クリックすると文字が変わります</button>
<p>{{ first_str }}</p>
</div>
var vm = new Vue({
el: '#app-7',
data: {
first_str: '最初に表示されています',
clicked_str: 'ボタンが押されました'
},
methods:{
change: function() {
this.first_str = this.clicked_str
}
}
})
v-on(その2:引数とイベント)
- v-onでメソッドを実行する際に、引数を渡すことが可能。
change('モノタス')
- DOMイベントをそのまんま渡すことも可能。
$event
という特別な変数を指定する。change($event, 'モノタス')
サンプルコード:引数とイベントオブジェクトを渡した時の例
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-8">
<!-- $eventでイベントオブジェクト渡します -->
<button v-on:click="change($event, '株式会社モノタス')">クリックすると文字が変わります</button>
<p>{{ first_str }}</p>
<p>{{ event_name }}</p>
</div>
var vm = new Vue({
el: '#app-8',
data: {
first_str: '最初に表示されています',
clicked_str: 'ボタンが押されました',
event_name: 'イベントが表示されます'
},
methods:{
change: function(event, msg) {
this.first_str = msg
this.event_name = event.target.tagName
}
}
})
v-on(その3:イベントをJavaScriptから指定)
- click等のDOMイベントは、JavaScriptからも指定できる
*サンプルコード:その2のコードをちょっと変更
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-8-2">
<!-- vm_eventに変更 -->
<button v-on:[vm_event]="change($event, '株式会社モノタス')">クリックすると文字が変わります</button>
<p>{{ first_str }}</p>
<p>{{ event_name }}</p>
</div>
var vm = new Vue({
el: '#app-8-2',
data: {
first_str: '最初に表示されています',
clicked_str: 'ボタンが押されました',
event_name: 'イベントが表示されます',
vm_event: 'click' // このイベントを設定
},
methods:{
change: function(event, msg) {
this.first_str = msg
this.event_name = event.target.tagName
}
}
})
v-on(その4:引数となるDOMイベント)
DOMイベントの一覧は以下を参照。
https://developer.mozilla.org/ja/docs/Web/Events
上記のページの内、以下をよく使うと想定。
- キーボードイベント
- マウスイベント
- ドラッグ & ドロップイベント
- フォームイベント
- フォーカスイベント
v-model(その1:基本)
- Vueのインスタンスの変更をリアルタイムにDOM要素へ展開してくれる
- 逆も然りであり、DOM要素の更新をVueインスタンスへ展開してくれる
- 双方向バインディング
- 使う頻度は超スーパーすげー高そう。
- 省略系→なし
サンプル:DOM要素を更新した時に、Vueインスタンスの情報が更新されることの例
テキスト
- inputタグで、
v-model
を使った場合と、使わずにv-bind
とv-on
を使った場合で、同じことができる。 -
v-model
の方がシンプル。
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9">
<p>タイトル「v-bind + v-on で対応」</p>
<input
v-bind:value="message_1"
v-on:input="message_1 = $event.target.value"
>
<p>表示:{{ message_1}}</p>
<hr>
<p>タイトル「v-model で対応」</p>
<input v-model="message_2">
<p>表示:{{ message_2}}</p>
</div>
var vm = new Vue({
el: '#app-9',
data: {
message_1: 'v-bind + v-on で対応',
message_2: 'v-model で対応'
}
})
テキストエリア
- 改行もちゃんと反映される
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9-1">
<textarea
id="ta"
cols="30"
rows="10"
v-model="msg1"
></textarea>
<pre>{{msg1}}</pre>
</div>
var vm = new Vue({
el: '#app-9-1',
data: {
msg1: '初期文字列'
}
})
チェックボックス
- 単一チェックボックスと、複数チェックボックスで、dataのtypeが変わるので注意。
単一チェックボックス
- チェックすることで、true/falseが切り替えられる
- dataのtypeはbooleanとなる
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9-1-2">
<input
type="checkbox"
id="hasBoll"
v-model="hasBoll"
>
<lavel for="hasBoll">ボール持ってる</lavel>
<p>
{{hasBoll}}
</p>
</div>
var vm = new Vue({
el: '#app-9-1-2',
data: {
hasBoll: false // 型がboolan
}
})
複数チェックボックス
- チェックボックスの**
v-model
に同じフィールド名を設定する**と、勝手にグルーピングしてくれる。 - チェックすることでvalueが配列に設定される
- dataのtypeが配列になる
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9-1-3">
<input
type="checkbox"
id="1"
v-model="birthMonthList"
value="1月~4月"
>
<label for="1">1〜4月</label>
<input
type="checkbox"
id="2"
v-model="birthMonthList"
value="5月~8月"
>
<label for="2">5〜8月</label>
<input
type="checkbox"
id="3"
v-model="birthMonthList"
value="9月~12月"
>
<label for="3">9〜12月</label>
<p>
{{ birthMonthList }}
</p>
</div>
var vm = new Vue({
el: '#app-9-1-3',
data: {
birthMonthList: []
}
})
ラジオボタン
- ラジオボタンの**
v-model
に同じフィールド名を設定する**と、勝手にグルーピングしてくれる。 - チェックすることでvalueが配列に設定される
- dataのtypeが文字列になる
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9-1-4">
<input
type="radio"
id="1"
v-model="birthMonth"
value="1月~4月"
>
<label for="1">1〜4月</label>
<input
type="radio"
id="2"
v-model="birthMonth"
value="5月~8月"
>
<label for="2">5〜8月</label>
<input
type="radio"
id="3"
v-model="birthMonth"
value="9月~12月"
>
<label for="3">9〜12月</label>
<p>
{{ birthMonth }}
</p>
</div>
var vm = new Vue({
el: '#app-9-1-4',
data: {
birthMonth: ""
}
})
セレクトボックス
- 単一選択セレクトボックスと、複数選択セレクトボックスで、dataのtypeが変わるので注意。
単一選択セレクトボックス
- dataのtypeはStringとなる
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9-1-5">
<span>出身地</span>
<select v-model="birthArea">
<option v-for="item in areas" :key="item">{{item}}</option>
</select>
<p>
{{birthArea}}
</p>
</div>
var vm = new Vue({
el: '#app-9-1-5',
data: {
birthMonth: "",
areas: ["荒川区", "台東区", "足立区"],
birthArea: '荒川区'
}
})
複数選択セレクトボックス
- dataのtypeは配列となる
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9-1-6">
<span>出身地</span>
<!-- multipleをつける -->
<select v-model="birthArea" multiple>
<option v-for="item in areas" :key="item">{{item}}</option>
</select>
<p>
{{birthArea}}
</p>
</div>
var vm = new Vue({
el: '#app-9-1-6',
data: {
birthMonth: "",
areas: ["荒川区", "台東区", "足立区"],
birthArea: []
}
})
v-model(その2:修飾子)
.lazy修飾子
- カーソルが離れた瞬間に、DOM要素の更新をVueインスタンスへ展開してくれる。
- メールアドレスの入力とか、全て入力してからバリデーションさせるものについて有効かと。
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9-2">
<!-- .lazy修飾子 -->
<input v-model.lazy="message">
<p>表示:{{ message }}</p>
</div>
var vm = new Vue({
el: '#app-9-2',
data: {
message: '',
}
})
.number修飾子
-
input
の種別がnumber
だったとしても、入力内容が更新されるとStringとして判断されてしまう。 -
.number
修飾子をつけると、この問題を解決してくれるので、数値入力のinputには.number
修飾子をつけるのがベター。
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9-3">
<!-- number修飾子つけないと、変更時にStringに・・・ -->
num1:<input
type="number"
v-model="num1"
>
<p>num1:(表示){{ num1 }}</p>
<p>num1:(型){{ typeof num1 }}</p>
<hr>
<!-- number修飾子つけると、numberのまま -->
num2:<input
type="number"
v-model.number="num2"
>
<p>num2:(表示){{ num2 }}</p>
<p>num2:(型){{ typeof num2 }}</p>
</div>
var vm = new Vue({
el: '#app-9-3',
data: {
num1: 0,
num2: 0
}
})
.trim修飾子
- 前後の空白をtrimしてくれる
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9-4">
trimなし:<input
type="string"
v-model="msg1"
>
<pre>{{ msg1 }}</pre>
<hr>
trimあり:<input
type="string"
v-model.trim="msg2"
>
<pre>{{ msg2 }}</pre>
</div>
var vm = new Vue({
el: '#app-9-4',
data: {
msg1: '初期文字列',
msg2: '初期文字列'
}
})
v-if / v-else / v-else-if
- if文用のディレクティブ。他の言語と同様、if, else,else-if がある。
- 条件付きレンダリングの一つ
- 使う頻度は・・・もちろん高いが、複雑になりすぎないように元々の設計が大事そう。
- 省略系→なし
ルール
v-if,v-else,v-else-ifはセットで使うようにと、以下のルールがある。
v-else 要素は、v-if または v-else-if 要素の直後になければなりません。それ以外の場合は認識されません。
v-else と同様に、v-else-if 要素は v-if 要素またはv-else-if 要素の直後になければなりません。
サンプルコード:単純な例
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-10">
<input v-model="message">
<button @click="change()">クリックすると文字が変わるかもしれません。</button>
<p v-if="changeStr==='1'">
1が押下されました
</p>
<p v-else-if="changeStr==='2'">
2が押下されました
</p>
<p v-else-if="changeStr==='3'">
3が押下されました
</p>
<p v-else>
1~3の間の数字を押してください。
</p>
</div>
var vm = new Vue({
el: '#app-10',
data: {
message: '',
changeStr: ''
},
methods:{
change: function() {
this.changeStr = this.message
}
}
})
templateを使ってグループ化もできます。以下、例。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-show
- 条件付きレンダリングの一つ
-
v-if
と同じように使えるが、else的なディレクティブは存在しない。 - 省略系→なし
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-10">
<input v-model="message">
<button @click="change()">クリックすると文字が変わるかもしれません。</button>
<p v-show="changeStr==='1'">
1が押下されました
</p>
<p v-show="changeStr==='2'">
2が押下されました
</p>
<p v-show="changeStr==='3'">
3が押下されました
</p>
</div>
v-for(その1:基本)
- for文用のディレクティブ。
- リストレンダリングと呼ばれる、配列を扱うディレクティブの一つ。
- 配列でもオブジェクト(kotlinでいうとMap)でもOK。
- 配列の場合、2つめの引数がindex(任意)
- オブジェクトの場合、2つめの引数がオブジェクトのキー(任意)、3つめの引数がindex(任意)
- 使う頻度はもち高い。
-
【超重要】必ずkey属性を指定する。
- とにかくkeyを指定する笑。理由は こちら(キーつき v-for)を参照してください。
- 省略系→なし
サンプルコード:いろいろな書き方
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-11">
<p>example-1:オブジェクトのみ</p>
<ul id="example-1">
<!-- 書き方は様々 -->
<!-- keyを忘れない-->
<li v-for="item in items" :key="item.message">
{{ item.message }}
</li>
</ul>
<hr>
<p>example-2:インデックスも。</p>
<ul id="example-2">
<!-- インデックスを使える -->
<!-- keyを忘れない-->
<li v-for="(item, index) in items" :key="item.message">
{{ item.message }}:インデックス→{{ index }}
</li>
</ul>
<hr>
<p>example-3:オブジェクトにて</p>
<ul id="example-3">
<!-- マップもOK -->
<!-- keyを忘れない-->
<li v-for="(value, key, index) of object" :key="value">
{{ index }}. {{ key }}: {{ value }}
</li>
</ul>
</div>
var vm = new Vue({
el: '#app-11',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
],
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
})
v-for(その2:その他)
templateタグも使える&回数指定のループも大丈夫。
<!-- template -->
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
<!-- 回数指定 -->
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
番外編:v-show
とv-if
のどちらを使うか?
-
v-if
- DOM自体を生成・破棄される。
- template使える。
- DOM自体を作成しないパターンがあるため、初期描画は早い。
- コンポーネントの表示・非表示の切り替えの場合は、DOMの生成・破棄を実行するため、遅い。
-
v-show
-
display none
を指定して、画面に表示されないようになる。 - templateは使えない。
- DOM自体は作ってしまうため、初期描画が遅い。
- コンポーネントの表示・非表示の切り替えの場合は、
display none
を切り替えするだけなので早い。
-
- 頻繁に表示・非表示を切り替える場合は、
v-show
を使う。それ以外の場合は、v-if
を使う
番外編:v-for
とv-if
の同時使用は禁止
v-if と v-for を同時に利用することは 推奨されません。 詳細については スタイルガイド を参照ください。
v-if といっしょに使用されるとき、v-for は v-if より優先度が高くなります。詳細については リストレンダリング のガイドを参照してください。
とのこと。
v-slot
- Vueコンポーネントのデータ受け渡しで使う
- 詳しくはこちら