2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vue.js を1から学んでみた〜ディレクティブ〜

Last updated at Posted at 2020-07-28

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構文({{}}で囲んだコード)と一緒。
  • 省略系→{{}}

サンプルコード

sample1.html
<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>
sample1.js
var vm = new Vue({
  el: '#app-1',
  data: {
    message: 'Hello Vue.js!',
    msg: 'サンキュー'
  },
  methods: {
    showMessage: function () {
      return "はろー、Vue.js"
    },
    showMsg: function () {
      return "Thank you!!"
    }
  }
})

結果
image.png

v-once

  • 1度DOM展開したら、そのあとは変更させない。
  • なんかの処理で、バインディングしたデータが変更されても、描画を変更させたくないときに有効。
    • 個人的にはこれを使うのは最終手段かなと。感覚的に。
  • 省略系→なし

サンプル(v-onceを使わない)

sample2-1.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-2">
  <p v-text="message"></p>
  <p v-text="showMessage()"></p>
</div>
sample2.js
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()で書き換えられて表示される)
image.png

サンプル(v-onceを使う・JavaScriptは変更しないので割愛)

sample2-2.html
<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>

結果(「Hello Vue.js!」が表示される)
image.png

v-html

  • DOMの内側に展開。
    • v-textは「文字列」で展開する
    • v-htmlは「HTML」で展開する
  • HTMLをそのまま展開されるので、XSSに注意して使うことが必要。
  • 省略系→なし

サンプルコード:h1タグを表示させたいときー

sample3.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-3">
  <p v-text="message"></p>
  <p v-html="message"></p>
</div>
sample3.js
var vm = new Vue({
  el: '#app-3',
  data: {
    message: '<h1>タイトルです<h1>'
  }
})

結果
image.png

v-bind(その1:属性値)

  • htmlタグの属性に埋め込む時に使用。
  • 引数が必要なディレクティブ。引数は:の後ろに設定するv-bind:href="url
  • 使う頻度がスーパー高そう。
  • 省略系→:href='url'のように、コロンだけでOK。

サンプルコード:aタグのリンク先を設定したいときー

sample4.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-4">
  <!-- :(コロン)の後に引数を設定する-->
  <a v-bind:href="url">モノタス</a>


  <!-- 省略系
  <a :href="url">モノタス</a>
  -->
</div>
sample4.js
var vm = new Vue({
  el: '#app-4',
  data: {
    url: 'https://www.monotas.net/'
  }
})

結果
aaa.gif

v-bind(その2:属性)

  • htmlタグそのものを定義することも可能
  • 属性を[]で囲うことで指定可能。例:<a :[attr]="hogehoge">
  • つまり、v-bindだけで、各種タグに属性とそのvalueのバインディングができる。
  • これも使う頻度がスーパー高そう。
sample5.html
<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>
sample5.js
var vm = new Vue({
  el: '#app-5',
  data: {
    attr: 'href',
    monotas_url: 'https://www.monotas.net/',
    jta_url: 'https://www.jta-tennis.or.jp/'
  }
})

結果
aaa2.gif

v-bind(その3:オブジェクト化)

  • 1つのタグに複数のv-bindを指定することもできるが、一つのオブジェクトとして記載することも可能。
  • Vueフィールドにオブジェクトを用意して、それをv-bindで指定することも可能。
  • 見た目すっきり系でもあるし、DOMの操作しやすくなりそうな感覚。まだふんわりとしかわかってない。
sample6.html
<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>
sample6.js
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/'
    }
  }
})

結果:全て同じIDとurlが設定されている
image.png
image.png

v-on(その1:基本)

  • クリック等のDOMが提供しているイベントのリスナー。
  • イベント発火時のJavaScriptの実行が可能。
  • 引数が必要なディレクティブ。引数は: の後ろに設定するv-on:click="change"
    • clickはDOMが提供しているイベント
    • changeが Vueで定義したメソッド。
  • 使う頻度は超スーパー高そう。
  • 省略系→@click="change"のように、コロンだけでOK。

サンプルコード:clickイベントの例。ボタンが押されたら文字列を変更します的なときー

sample7.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-7">
  <button v-on:click="change">クリックすると文字が変わります</button>
  <p>{{ first_str }}</p>
</div>
sample7.js
var vm = new Vue({
  el: '#app-7',
  data: {
    first_str: '最初に表示されています',
    clicked_str: 'ボタンが押されました'
  },
    methods:{
      change: function() {
      	this.first_str = this.clicked_str
      }
    }
})

結果
aaa3.gif

v-on(その2:引数とイベント)

  • v-onでメソッドを実行する際に、引数を渡すことが可能。change('モノタス')
  • DOMイベントをそのまんま渡すことも可能。$eventという特別な変数を指定する。change($event, 'モノタス')

サンプルコード:引数とイベントオブジェクトを渡した時の例

sample8.html
<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>
sample8.js
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
      }
    }
})

結果
aaa4.gif

v-on(その3:イベントをJavaScriptから指定)

  • click等のDOMイベントは、JavaScriptからも指定できる

*サンプルコード:その2のコードをちょっと変更

sample8-2.html
<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>
sample8-2.js
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-bindv-onを使った場合で、同じことができる。
  • v-modelの方がシンプル。
sample9.html
<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>
sample9.js
var vm = new Vue({
  el: '#app-9',
  data: {
    message_1: 'v-bind + v-on で対応',
    message_2: 'v-model で対応'
  }
})

結果
aaa7.gif

テキストエリア

  • 改行もちゃんと反映される
sample9-1.html
<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>
sample9-1.js
var vm = new Vue({
  el: '#app-9-1',
  data: {
    msg1: '初期文字列'
  }
})

結果
aaa16.gif

チェックボックス

  • 単一チェックボックスと、複数チェックボックスで、dataのtypeが変わるので注意。

単一チェックボックス

  • チェックすることで、true/falseが切り替えられる
  • dataのtypeはbooleanとなる
sample9-1-2.html
<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>
sample9-1-2.js
var vm = new Vue({
  el: '#app-9-1-2',
  data: {
    hasBoll: false // 型がboolan
  }
})

結果
aaa17.gif

複数チェックボックス

  • チェックボックスの**v-modelに同じフィールド名を設定する**と、勝手にグルーピングしてくれる。
  • チェックすることでvalueが配列に設定される
  • dataのtypeが配列になる
sample9-1-3.html
<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>
sample9-1-3.js
var vm = new Vue({
  el: '#app-9-1-3',
  data: {
    birthMonthList: []
  }
})

結果
aaa18.gif

ラジオボタン

  • ラジオボタンの**v-modelに同じフィールド名を設定する**と、勝手にグルーピングしてくれる。
  • チェックすることでvalueが配列に設定される
  • dataのtypeが文字列になる
sample9-1-4.html
<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>
sample9-1-4.js
var vm = new Vue({
  el: '#app-9-1-4',
  data: {
    birthMonth: ""
  }
})

結果
aaa18.gif

セレクトボックス

  • 単一選択セレクトボックスと、複数選択セレクトボックスで、dataのtypeが変わるので注意。

単一選択セレクトボックス

  • dataのtypeはStringとなる
sample9-1-5.html
<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>
app9-1-5.js
var vm = new Vue({
  el: '#app-9-1-5',
  data: {
    birthMonth: "",
    areas: ["荒川区", "台東区", "足立区"],
    birthArea: '荒川区'
  }
})

結果
aaa18.gif

複数選択セレクトボックス

  • dataのtypeは配列となる
sample9-1-6.html
<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>
sample9-1-6.js
var vm = new Vue({
  el: '#app-9-1-6',
  data: {
    birthMonth: "",
    areas: ["荒川区", "台東区", "足立区"],
    birthArea: []
  }
})

結果
aaa19.gif

v-model(その2:修飾子)

.lazy修飾子

  • カーソルが離れた瞬間に、DOM要素の更新をVueインスタンスへ展開してくれる。
  • メールアドレスの入力とか、全て入力してからバリデーションさせるものについて有効かと。
sample9-2.html
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app-9-2">
  <!-- .lazy修飾子 -->
  <input v-model.lazy="message">
  <p>表示:{{ message }}</p>
</div>
app9-2.js
var vm = new Vue({
  el: '#app-9-2',
  data: {
    message: '',
  }
})

結果
aaa13.gif

.number修飾子

  • inputの種別がnumberだったとしても、入力内容が更新されるとStringとして判断されてしまう。
  • .number修飾子をつけると、この問題を解決してくれるので、数値入力のinputには.number修飾子をつけるのがベター。
sample9-3.html
<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>
sample9-3.js
var vm = new Vue({
  el: '#app-9-3',
  data: {
    num1: 0,
    num2: 0
  }
})

結果
aaa14.gif

.trim修飾子

  • 前後の空白をtrimしてくれる
sample9-4.html
<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>
sample9-4.js
var vm = new Vue({
  el: '#app-9-4',
  data: {
    msg1: '初期文字列',
    msg2: '初期文字列'
  }
})

結果
aaa15.gif

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 要素の直後になければなりません。

サンプルコード:単純な例

sample10.html
<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>
sample10.js
var vm = new Vue({
  el: '#app-10',
  data: {
    message: '',
    changeStr: ''
  },
  methods:{
    change: function() {
      this.changeStr = this.message
    }
  }
})

結果
aaa9.gif

templateを使ってグループ化もできます。以下、例。

sample10-1.html
<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

v-show

  • 条件付きレンダリングの一つ
  • v-ifと同じように使えるが、else的なディレクティブは存在しない。
  • 省略系→なし
v-show.html
<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属性を指定する
  • 省略系→なし

サンプルコード:いろいろな書き方

sample-11.html
<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>
sample-11.js
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'
    }
  }
})

結果
image.png

v-for(その2:その他)

templateタグも使える&回数指定のループも大丈夫。

sample10-1.html
<!-- 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-showv-ifのどちらを使うか?

  • v-if
    • DOM自体を生成・破棄される。
    • template使える。
    • DOM自体を作成しないパターンがあるため、初期描画は早い。
    • コンポーネントの表示・非表示の切り替えの場合は、DOMの生成・破棄を実行するため、遅い。
  • v-show
    • display noneを指定して、画面に表示されないようになる。
    • templateは使えない。
    • DOM自体は作ってしまうため、初期描画が遅い。
    • コンポーネントの表示・非表示の切り替えの場合は、display noneを切り替えするだけなので早い。
  • 頻繁に表示・非表示を切り替える場合は、v-showを使う。それ以外の場合は、v-ifを使う

番外編:v-forv-ifの同時使用は禁止

v-if と v-for を同時に利用することは 推奨されません。 詳細については スタイルガイド を参照ください。
v-if といっしょに使用されるとき、v-for は v-if より優先度が高くなります。詳細については リストレンダリング のガイドを参照してください。

とのこと。

v-slot

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?