Vue.js　勉強メモ　こういう事が知りたかった！を中心に

## はじめに
仕事で使う事になったので1からVue.jsについて学んだ際にちゃんと覚えておかないとまずそうな事がいくつかあったのでそれを備忘録として残しておく。

## Index（各記事へのリンク）
- [【Vue.js】computedとmethodsの違い](https://qiita.com/yuta-katayama-23/items/cb1ba764d3ceddb64f9c)
- [【Vue.js】computedとwatchの使い分け](https://qiita.com/yuta-katayama-23/items/65f0183e4ef9de699a89)
- [【Vue.js】v-ifとv-showの違い](https://qiita.com/yuta-katayama-23/items/d06a1d059e051c5c2099)
- [【Vue.js】v-forを使う時の注意点 key属性](https://qiita.com/yuta-katayama-23/items/ea5df45d93d2bed8b42c)
- [【Vue.js】Vue.jsでリアクティブがどう実現されるか](https://qiita.com/yuta-katayama-23/items/5cbc82e013609e0f6c00)
- [【Vue.js】Vueインスタンスのライフサイクル](https://qiita.com/yuta-katayama-23/items/578bb0cecf165fd850db)
- [【Vue.js 2.x】なぜコンポーネントではdataプロパティは関数なのか？](https://qiita.com/yuta-katayama-23/items/f0e100b33f609a172962)
- [【Vue.js】コンポーネントはLocal登録で使う](https://qiita.com/yuta-katayama-23/items/fa176f65162e6d4dd2cb)
- [【Vue.js】$emitメソッドの正体　コンポーネント(子→親)の間のデータ受け渡し](https://qiita.com/yuta-katayama-23/items/5e0396df42a95ef96239)
- [【Vue.js】コンポーネントはパスカルケースで書くのがオススメ](https://qiita.com/yuta-katayama-23/items/18422f18049dcd00bc25)
- [【Vue.js】名前付きslotを用いている時のデフォルトslotが適用される仕組み](https://qiita.com/yuta-katayama-23/items/1a9b8e7eb5700b56110b)
- [【Vue.js】動的にコンポーネントを切り替える際に、切り替え毎にdestroyedされないようにするには](https://github.com/yuta-katayama-23/intro-vuecli/tree/8373cd49df7eb9d18b773a1276bf91549309c362)
- [【Vue.js】v-modelの修飾子の種類とその使いどころ](https://qiita.com/yuta-katayama-23/items/317c7aa4773497c0506e)
- [【Vue.js】親・子のコンポーネント間でのv-modle](https://qiita.com/yuta-katayama-23/items/9c556de924b184bcf2b3)
- [【Vue.js】フィルターとcomputedのキャッシュの違い](https://qiita.com/yuta-katayama-23/items/b5dca4fac48a6e0af8b8)
- [【Vue.js】ミックスインの使い方と注意](https://qiita.com/yuta-katayama-23/items/6ed91b228f3f85152711)
- [【Vue.js】トランジション(アニメーション)の使い方](https://qiita.com/yuta-katayama-23/items/287b740aadd15ac1085e)
- [【Vue.js】複数要素を切り替えるトランジションの注意　key属性とmode属性](https://qiita.com/yuta-katayama-23/items/bdb68b6da271178bce57)
- [【Vue.js】JavaScriptでトランジションを実装する](https://qiita.com/yuta-katayama-23/items/065be81dfa668deac903)
- [【Vue.js】router-linkのactive-class・exactの使い方](https://qiita.com/yuta-katayama-23/items/0a559c738f6c9d26b177)
- [【Vue.js】vue-routerの動的URLでパラメータが変わった時、ライフサイクルフックは呼ばれないので注意](https://qiita.com/yuta-katayama-23/items/fd170f08ac6f2d61e3fc)
- [【Vue.js】コンポーネントの3つのナビゲーションガード](https://qiita.com/yuta-katayama-23/items/2b7072d7521b20a5c223)
- [複数のVueインスタンスを作成する際の注意](#複数のvueインスタンスを作成する際の注意)
- [Vueインスタンスのプロパティ](#vueインスタンスのプロパティ)
- [$mountメソッドの利用場面](#mountメソッドの利用場面)
- [Vue.jsにおけるHTML描画の方法3つ](#vuejsにおけるhtml描画の方法3つ)
- [render関数とVue.jsの仮想DOM](#render関数とvuejsの仮想dom)
- [親・子間のデータ（slotを介すものも含む）受け渡しを行う際にはこれが基本パターン](#親子間のデータslotを介すものも含む受け渡しを行う際にはこれが基本パターン)

## お勉強リポ
細かくコミットを区切っているので、Vue.jsのポイントとなる概念・構文についてそれぞれをソースベースで見れる。

https://github.com/yuta-katayama-23/intro-vuejs

https://github.com/yuta-katayama-23/intro-vuecli

## Vue API レファレンス
https://jp.vuejs.org/v2/api/

## 複数のVueインスタンスを作成する際の注意
以下のように、複数のVueインスタンスを作成する事はできるが、

- 相互に独立しており、依存関係（関連性）がない場合

に基本的には使うようにする。Aというインスタンスの値が変更されたらBというインスタンスに反映させる、という事は技術的には可能だが、複雑になるのでやらないようにすべき。

```html:sample.html
<body>
    <div id="app1">
        <p>{{message}}</p>
    </div>

    <div id="app2">
        <p>{{message}}</p>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
    <script>
        new Vue({
            el: '#app1',
            data: {
                message: 'インスタンス1'
            }
        })

        new Vue({
            el: '#app2',
            data: {
                message: 'インスタンス2'
            }
        })
    </script>
</body>
```

## Vueインスタンスのプロパティ
VueインスタンスにはVue側であらかじめ用意されているプロパティがあり、`$`で始まるものがそれ。
（以下は`console.log()`でVueインスタンスの中身を出力させた結果。）

| 例1 | 例2 |
|:-:|:-:|
| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/70195de7-c2d6-746f-32c5-4b146924ecec.png) | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/be7dbe8a-56cc-57a0-2fea-a3baedb149f7.png) |

これらのプロパティへはインスタンスの内部・外部の両方から以下のようにアクセスできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/9e2e91fd-6bd4-c0c6-b682-643d83b6a858.png)

```html:sample.html
<body>
    <div id="app1">
        <p>{{message}}</p>
        <p>{{name}}</p>
        <p>{{myData}}</p>
    </div>

    <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
    <script>
        const data = {
            message: 'こんにちは',
            name: 'こんばんは'
        }

        const vm = new Vue({
            el: '#app1',
            data: data,
            computed: {
                myData: function () {
                    // プロパティへ内部からアクセスの例。"this.$○○"の形式でアクセスする。
                    return this.$data;
                }
            }
        })

        // Vueインスタンスのプロパティへ外部からアクセスの例。以下は"true"になる。
        console.log(data === vm.$data);
    </script>
</body>
```

※Vueインスタンスのプロパティの詳細は[こちらを参照](https://jp.vuejs.org/v2/api/#%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3)

## $mountメソッドの利用場面
`$mountメソッド`を用いると、Vueインスタンス作成後に`elプロパティ`を設定できるが、これは

- 動的にVueインスタンスの対象を変更したい
- Vueインスタンスが作成された時にはまだHTML上に要素がないようなものに対し後からマウントする（表示させる）

といった場面で用いる。

※ただし、基本的にはVueインスタンス作成時に`elプロパティ`で指定するようにする

```html:sample.html
<body>
    <div id="app">
        <p>{{message}}</p>
    </div>

    <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
    <script>
        const vm = new Vue({
            data: {
              message: 'こんにちは'
            },
        })
        vm.$mount('#app')
    </script>
</body>
```

※実は`mount`は、[仮想DOM](#仮想domとは)を実際のDOMに反映させるという事をしている。（elプロパティでも同じ）。これにより仮想DOMがDOMとして構築され実際に画面が描画されるという仕組みになっている。

## Vue.jsにおけるHTML描画の方法3つ
Vue.jsにはHTMLをレンダリングさせる方法が3つある。それぞれ実装方法は違うがいずれも同じように画面が描画される。
※このそれぞれの方法でのHTML描画を`template`と呼んだりし、[templateを`render関数に変換する](#実はrender関数以外のhtml描画の方法も内部的な動きはrender関数と同じ)と言ったりする。

| # | やり方 |
|:-:|---|
| 1 | HTMLに直接記述  |
| 2 | templateプロパティに文字列を記述 |
| 3 | render関数でVNode（仮想ノード）をreturn |

### 1　HTMLに直接記述
今までやってきたように直接記述する。

```html:sample.html
<body>
    <div id="app1">
        <h1>こんにちは、{{name}}</h1>
    </div>

    <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
    <script>
        new Vue({
            el: '#app1',
            data: {
              name: '田中 太郎'
            }
        })
    </script>
</body>
```

### 2　templateプロパティに文字列を記述
以下のように`templateプロパティ`に文字列を記述する。

```html:sample.html
<body>
    <div id="app2"></div>

    <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
    <script>
        new Vue({
            el: '#app2',
            data: {
              name: '田中 太郎'
            },
            template: '<h1>こんにちは、{{name}}</h1>'
        })
    </script>
</body>
```

### 3　render関数でVNode（仮想ノード）をreturn
以下のように`renderプロパティ`で`VNode（仮想ノード）`をreturnさせる。

```html:sample.html
<body>
    <div id="app3"></div>

    <script src=" https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
    <script>
        new Vue({
            el: '#app3',
            data: {
              name: '田中 太郎'
            },
            render: function (h) {
                return h('h1', `こんにちは、${this.name}`);
            }
        })
    </script>
</body>
```

## render関数とVue.jsの仮想DOM
render関数の部分で`VNode（仮想ノード）`という話が出たが、これは速度向上などのためにVue.js側で備えている仕組みである仮想DOMに関連するもの。この仮想DOMについて背景にある問題も踏まえてまとめておく。

### 前提
document（ブラウザのオブジェクト）に直接アクセスして、それに対し要素の新規作成・更新・削除したりする処理は遅い。
そのため何かが変更されるたびにDOMを全てを再描画していたのでは処理が遅くWebサイトとして使用感が悪いものになる。

### ではどうするか？
- 変更の前後でDOMの差分を見比べ、差分がある部分だけ実際のDOM書き換える

というように、変更された部分だけDOMを書き換えるという方法が考えられる。
この方法（仕組み）を実現するために`仮想DOM`というものがある。
つまりVue.jsでは、変更の前後で仮想DOMを比較し差分があるか？をチェックし、差分があればその特定個所のみ実際のDOMを変更する、という仕組みにする事で高速化を実現している。

### 仮想DOMとは？
- DOM要素を模したJavaScriptのオブジェクト

であり、JavaScriptのオブジェクトであるが故にそこへアクセスは高速に行えるので高速化が実現できる。仮想DOMはVNode（仮想ノード）の集合体のイメージでよく、ノード=節という事から分かるようにノードはDOMを構成する要素。

### 仮想DOMがないとどうなるか？
仮想DOMがない場合を考えると、Vue.jsでどこが変更されたか？の特定ができなくなり、変更毎に変更後のものに基づきDOMを全部再描画しないといけなくなり処理が遅くなる（使用感が悪くなる）。
言い換えると、**バニラJavaScriptで明示的にDOMの一部のみを変更した時と同じ速度で、Vue.jsでもあらゆる要素の変更に対する再描画を行うために仮想DOMがある。**

※バニラJavaScriptで明示的にDOMの一部のみを変更した時　とは以下のようなソースコードの処理の事。

```html:sample.html
<body>
    <div id="app1"></div>
</body>
```

```js:sample.js
const app = document.querySelector('#app'); 
app.innerHTML = '<p>Hello</p>';
```

### Vue.jsにおける仮想DOMのメリット

- Webサイトの動作の高速化（使用感の向上）
- render関数が使える
- ブラウザ以外の環境に対応しやすい
- 変更前後の仮想DOMを比較し変わっていなければ何もしないという処理も実現可能

### documentと仮想DOMというそれぞれのオブジェクトの違い
| # | 説明 | 特徴 |
|:-:|---|---|
| document | ブラウザが標準で備えているオブジェクト| ここへのアクセスは処理コストが高く遅い |
| 仮想DOM | JavaScriptのオブジェクト | ここへのアクセスは処理コストは低く速い |

### 実はrender関数以外のHTML描画の方法も内部的な動きはrender関数と同じ
[前セクション（Vue.jsにおけるHTML描画の3つの方法）](#vuejsにおけるhtml描画の仕組み)で3つの実装方法を見たが、実は内部的な動きでは**1, 2もrender関数が動きVNode(仮想ノード)化されて**、それがVueに渡され仮想DOMになり、最終的にDOMに変換されて画面に描画される。

## 親・子間のデータ（slotを介すものも含む）受け渡しを行う際にはこれが基本パターン
基本的には、以下の順によく使われる。

1. 親→子へのデータ渡し・html要素の渡し（props・template/slot）
1. 子→親のデータ渡し（$emit）
1. 子→親のデータ渡し（template・slotの場合のslotプロパティ）

※できるだけslotでデータを子→親で渡すとかはしないほうがいい。
※できる事・機能と実際にどのようにプロダクトコードを書いていくのか？は別物であり、できるだけシンプルに書く方が後で楽になる。

## カスタムディレクティブの使い処
- コードの再利用を行いたい
- コンポーネントにするよりディレクティブの方が良い