【Vue.js 】 カスタムディレクティブ

# はじめに
カスタムディレクティブとは```v-bind```のようなディレクティブを自作するための機能である。
ディレクティブではDOMがどういった操作をするのかそれぞれの属性で決まっていたが、カスタムディレクティブではDOMの操作の仕組みをカスタマイズして定義することができる。

## ディレクティブとは？
テンプレートの記述できる```v-```から始まる属性のこと。
要素の表示/非表示、データバインディングなどといったDOM操作ができる。

## カスタムディレクティブの登録方法
グローバル、ローカルのどちらでも定義できる。

### ローカルへの登録
directivesオプションに登録することで特定のコンポーネントでカスタムディレクティブが使用できる。

```sample.vue
// example という名前のカスタムディレクティブを登録
new Vue({
  el: 'app',
  directives: {
    example: {
    }
  }
})
```

### グローバルへの登録
グローバルメソッド```Vue.directive```メソッドを使用してグローバルに登録することで全てのコンポーネントでカスタムディレクティブが使用できる。

```sample.vue
<script>
// example という名前のカスタムディレクティブを登録
Vue.directive('example',{
});
</script>
```

### 登録したカスタムディレクティブを使用
カスタムディレクティブとして登録したメソッド名に```v-（プレフィックス）```をつけて使用する。

```sample.vue
<template>
  <div v-example>カスタムディレクティブ</div>
  <!-- ↓ カスタムディレクティブのトリガとなるデータをバインドすることもできる -->
  <!-- valueが変化すれば必ず呼び出される -->
  <div v-example="value">カスタムディレクティブ</div>
</template>
```

#### 使用可能なフック
カスタムディレクティブはオプションで下記のフックが使用できる。
ここでいうフックとは処理をひっかけるようなこと。下記のタイミングで処理を実行する。

| メソッド名          | タイミング                          | 
|:-----------------|:---------------------------------|
| bind             | ディレクティブがはじめて要素と結びついた時 |
| inserted         | 紐づいた要素が親Nodeに挿入された時     |
| update           | 紐づいた要素を包含しているコンポーネントのVNodeが更新された時 |
| componentUpdated | 紐づいた要素を包含しているコンポーネントと子コンポーネントのVNodeが更新された時 |
| unbind           | 紐づいていた要素からディレクティブが削除される時 |

※ VNodeとは仮想DOMのNodeのこと。```update```と```componentUpdated```は仮想DOMが更新されたときに呼び出される。

それぞれの動作を確認してみる

```sample.vue
<div id="app">
  <input type="text" v-example>
</div>
```

```sample.vue
<script>
Vue.directive('example',{
  bind: function(el, binding) {
    console.log('bind')
  },
  inserted: function(el, binding) {
    console.log('inserted')
  },
  update: function(el, binding) {
    console.log('update')
  },
  componentUpdated: function(el, binding) {
    console.log('componentUpdated')
  },
  unbind: function(el, binding) {
    console.log('unbind')
  },
})
</script>
```
この状態でinputを操作すると```bind```や```inserted```のフックしか作動しない。
仮想DOMが更新されていないので```update```や```componentUpdated```は呼び出されない。

```sample.vue
<div id="app">
  <input type="text" v-model="value" v-example>
</div>
```
↑ のようにinputの値を動的にすることで仮想DOMが更新され、```update```や```componentUpdated```も呼び出すことができるようになり、データを監視しながらDOMを操作することができる。

#### フックの引数

| 引数              | 内容                                               | 
|:-----------------|:---------------------------------|
| el               | ディレクティブが付与されている要素       |
| binding          | バインドされた値、引数、修飾子のオブジェクト     |
| vnode            | 要素に対応するVNode |
| oldVnode         | 更新前のVnode(updat、componentUpdatedでのみ使用可)      |


##### 前の状態と比較して処理を行う
引数bindingにはバインドされたものに関するいくつかの情報が入っている。

bindingのもつプロパティの１つに```oldValue```と```value```がある。
```oldValue```は古いバインドデータを持っているので```oldValue```と```value```で新しいデータと古いデータを比較することができる。

↓ あるinputの値が変化したときの変更前と変更後の値が格納されるようにする

```sample.vue
<input type="text" v-model="value" v-example="value"/>
```
カスタムディレクティブにデータvalueを渡す。
これによりbindingのoldValueとvalueには変更前の値と変更後の値が格納される。

```sample.vue
<script>
Vue.directive('example',{
  update: function(el, binding) {
    console.log('update');
    // 古いデータ
    console.log('前回' + binding.oldValue);
    // 新しいデータ
    console.log('今回' + binding.value);
  }
})
</script>
```