Vue.js公式チュートリアルをゆっくり読んでいく2

# 前置き

[前回](https://qiita.com/jcong7495/items/f4acac6356902a2387f2)に引き続き、Vue.jsの[公式チュートリアル](https://jp.vuejs.org/v2/guide/index.html)をゆっくり読んでいきます。
チュートリアルに沿ってはいますが、サンプルはよりわかりやすく単純なものに一部変えているところもあります。

前回は主にVue.jsの[リアクティブシステム]([https://qiita.com/jcong7495/items/f4acac6356902a2387f2#%E3%83%AA%E3%82%A2%E3%82%AF%E3%83%86%E3%82%A3%E3%83%96%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%A8%E3%81%AF](https://qiita.com/jcong7495/items/f4acac6356902a2387f2#リアクティブシステムとは))に触れました。今回は、Vue.js上では「ディレクティブ」と呼ばれているVue.js特有の属性を見ていきたいと思います。

Vue.jsのバージョン：v2.6.11

# 今回のチュートリアル参照箇所

[はじめに - 宣言的レンダリング](https://jp.vuejs.org/v2/guide/index.html#%E5%AE%A3%E8%A8%80%E7%9A%84%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0)

[はじめに - 条件分岐とループ](https://jp.vuejs.org/v2/guide/index.html#%E6%9D%A1%E4%BB%B6%E5%88%86%E5%B2%90%E3%81%A8%E3%83%AB%E3%83%BC%E3%83%97)

[はじめに - ユーザー入力の制御](https://jp.vuejs.org/v2/guide/index.html#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E5%85%A5%E5%8A%9B%E3%81%AE%E5%88%B6%E5%BE%A1)

# v-bind:要素の紐付け

前回はVueインスタンスのdataプロパティに持たせたデータオブジェクトの内容を、elプロパティと紐付けた「**HTML要素の中で**」`{{ message }}`という形式で表示させました。

```vuejs
<div id="app">
    {{ message }}
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!'
        }
    });
</script>
```

この「**HTML要素の中で**」ということですが、HTML要素の属性部分には効くのでしょうか？
次のようにdataオブジェクトのmyFavoriteColorStyleに持たせたcssを#app要素のstyle属性に反映できるかやってみます。

```vuejs
<div id="app" style="{{ myFavoriteColorStyle }}">
    {{ message }}
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!',
            myFavoriteColorStyle: 'color:red',
        }
    });
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/8b750b71-cf78-909a-9f41-7ddab8d853d4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/44be0574-c379-4b99-9b26-abb7d1dec565.png)


style属性は反映されず、警告がでてしまいました。HTML属性部分には効かないようです。
しかし、Vue.jsが親切に警告内でどう書けばいいか教えてくれています。
`For example, instead of <div style="{{ val }}">, use <div :style="val">.`
これで書き直してみます。

```vuejs
<div id="app" :style="myFavoriteColorStyle"> <!-- {{ }} で囲まない -->
    {{ message }}
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!',
            myFavoriteColorStyle: 'color:red',
        }
    });
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/96e51bbb-57f8-abb2-00d3-86da9cb56d20.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/ee1a2556-ca18-e0e3-8e48-bfac57953e80.png)


今度は警告もでず、スタイルが適用されました。
ここで使用した`:style`ですが、正式には`v-bind:style`と書きます。`:style`は省略記法です。

v-bind属性はVue.js上ではディレクティブと呼ばれており、`v-bind:属性名`とすることで、
その属性に対してデータオブジェクトの内容を紐付けることができます。
もちろんこれもリアクティブなものなので、chromeのデベロッパーツールのコンソールで
`vm.myFavoriteColorStyle = "color:blue"`と打つと、すぐにそれが反映されます。

![myReactive.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/7596625a-eced-6d1b-260a-23f51e9d493c.gif)

# v-if:条件分岐

v-ifディレクティブは要素の表示、非表示を切り替えることができます。

```vuejs
<div id="app" v-if="seen">
    {{ message }}
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!',
            seen: true,
        }
    });
</script>
```

![v-if.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/9e9a80ad-58ad-a6a1-d661-82cce41d08ff.gif)

v-ifディレクティブは厳密には、要素の表示・非表示を切り替えるのではなく、要素を削除・再生成しています。
`vm.seen = false`したときにHTML要素を確認してみます。

![v-if2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/d396e5b9-1a6c-6849-7bd1-01a9e01c4bcd.gif)

要素の表示・非表示を切り替えるディレクティブは、v-ifの他にv-showもあります。
こちらは、要素のスタイルを`display:none`にするだけなので、要素自体は削除されません。
v-ifは要素を削除・再生成する一方、v-showはスタイルを切り替えるだけなので後者のほうが高速に動作します。

```vuejs
<div id="app" v-show="seen">
    {{ message }}
</div>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!',
            seen: true,
        }
    });
</script>
```

![v-show.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/4d3529b2-ca39-e573-6c53-8372cc21b02a.gif)

ところでチュートリアルにも記載がありますが、Vue.jsは上記のような要素の更新が行われたときにトランジション効果を
簡単につけることができます。例えばさきほどのv-ifディレクティブの例に、フェード効果をつけてみます。

```vuejs
<div id="app">
    <!-- トランジション効果をつけたい要素をVue.jsに予め用意されているtransitionタグ(コンポーネント)で囲みます -->
    <transition name="fade"> <!-- name属性で指定した名前を後述のstyleで使います -->
        <p v-if="seen">{{ message }}</p>
    </transition>
</div>

<style>
    /* 「transitionタグのname属性で指定した名前 + "-enter"」で要素が追加される直前、
       「transitionタグのname属性で指定した名前 + "-leave-to"」で要素が削除された直後を表すクラス名 */
    .fade-enter, .fade-leave-to {
        opacity: 0; /* 透明状態 */
    }
    /* 「transitionタグのname属性で指定した名前 + "enter-active"」で要素の追加中
       「transitionタグのname属性で指定した名前 + "leave-active"」で要素の削除中を表すクラス名 */
    .fade-enter-active, .fade-leave-active {
        transition: opacity .5s; /* 0.5秒かけて透明度を変化させる */
    }
</style>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!',
            seen: true,
        }
    });
</script>
```

![v-if_transition.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/2ca88e0d-46d4-4be8-ab55-a5549642fcd9.gif)

参考：[Enter/Leave とトランジション一覧 — Vue\.js](https://jp.vuejs.org/v2/guide/transitions.html)

# v-for:ループ

v-forは配列の内容を取り出して表示してくれます。

```vuejs
<ul id="app">
    <li v-for="message in messages">
        {{ message }}
    </li>
</ul>

<script>
    let messages = ['Hello','v-for','directive'];
    let vm = new Vue({
        el: '#app',
        data: {
            messages: messages,
        }
    });
</script>
```

![v-for.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/f245b16b-3ce4-0dcd-246e-07eb8cba90bc.gif)

もちろんリアクティブ。

# v-on:イベントハンドリング

v-onディレクティブはその要素に対してイベントリスナを設定してくれます。

```vuejs
<button id="app" v-on:click="changeMessage">
    {{ message }}
</button>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!',
        },
        methods: {
            changeMessage: function() { this.message = 'Hello Vue World!' },
        }
    });
</script>
```

![v-on.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/8c42b4ba-4c91-11cd-8b28-1750b17ac8ab.gif)

ところで、これをVue.jsを使わないで実現するとどうでしょうか。

```javascript
<button id="app" onclick="changeMessage()">
    Hello Vue!
</button>

<script>
    function changeMessage() {
        document.getElementById("app").innerHTML = 'Hello Vue World!';
    }
</script>
```

Vue.jsを使わなくてももちろんできます。むしろこれくらいならこちらのほうが全然シンプルです。
ではVue.jsを使うメリットは何でしょうか。次の場合を考えてみます。

例えば、button要素の中のテキストを赤くしたとします。

```javascript
<button id="app" onclick="changeMessage()">
    <span style="color:red">Hello Vue!</span>
</button>

<script>
    function changeMessage() {
        document.getElementById("app").innerHTML = 'Hello Vue World!';
    }
</script>
```

![v-on_not_use.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/e6e03644-dfa8-c4e7-2b56-b1153cfd9b7e.gif)

せっかく赤くしたテキストがクリックすると黒に戻ってしまいました。
innerHTMLは要素内のHTMLを設定するプロパティなので、テキスト部分だけ変更したい場合は、
例えば次のようにスクリプト部分を修正する必要があります。

```javascript
<button id="app" onclick="changeMessage()">
    <span style="color:red">Hello Vue!</span>
</button>

<script>
    function changeMessage() {
        document.querySelector("#app span").innerHTML = 'Hello Vue World!';
    }
</script>
```

![v-on_not_use2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566827/8c78628f-f04f-3419-3b53-03df4d3ad621.gif)

これでうまくいきました。でもこれは、「innerHTMLが要素内のHTMLを設定するプロパティであること」を知っていること、
それを知った上で、要件を満たすために、「querySelectorを使う」「Hello Vue!部分だけを書き換えるためにid="app"要素の
中のspan要素を指定するセレクタを記載する」など、DOMを操作するためのいくつかの追加の知識が必要です。

これをVue.jsで実現すると、

```vuejs
<button id="app" v-on:click="changeMessage">
    <span style="color:red">{{ message }}</span>
</button>

<script>
    let vm = new Vue({
        el: '#app',
        data: {
            message: 'Hello Vue!',
        },
        methods: {
            changeMessage: function() { this.message = 'Hello Vue World!' },
        }
    });
</script>
```

スクリプト部分は一切変更せずにできました。これはVue.jsがDOM操作をやってくれるからです。
Vue.jsを使うことで、DOM操作を気にせず、ロジックのみに集中できることがVue.jsのメリットの一つでもあります。

今回はここまでです。