基礎からメモ: Vue.js CH4-p137 カスタムディレクティブでデータを監視しながらDOMを操作する

カスタムディレクティブの仕組みを使って、v-bindのようなディレクティブを自作することができる。 ただしデータバインディングと違い仮想DOMではないので、$el$refsと同様に描画の最適化はされない。

使い方

カスタムディレクティブとしてメソッドを登録し、そのメソッド名にv-プレフィクスを付けてHTMLの中に使用する。

<div v-directive>example</div>

このカスタムディレクティブを呼び出すためのトリガーを値としてバインドすることもできる。次の場合は、valueが変化すると呼び出される。

<div v-directive="value">example</div>

ローカルへの登録

コンポーネントdirectivesオプションに登録すると、特定のコンポーネント内でのみ使える。

<div id="app">
  <input v-focus type="text">
</div>

この例では input要素にカスタムディレクティブv-focusが結び付けられているので、input要素がDOMに挿入されるときフォーカスが当てられる。

var app = new Vue({
  el: '#app',
  directives: {
    // 紐付いている要素(この場合はinput要素)がDOMに挿入されるとき
    // その要素にフォーカスを当てる
    focus: {
      inserted: function(el) {
        el.focus();
      }
    }
  }
})

グローバルへの登録

グローバルメソッドVue.directiveを使ってグローバルに登録されたカスタムディレクティブは、全てのコンポーネントから使える。

Vue.directive('focus', {
  inserted: function(el) {
    el.focus();
  }
})

使用可能なフック

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

updatecomponentUpdatedは、コンポーネントの仮想DOMが更新されたときに呼び出されるので、どこにもデータバインディングしていないデータが変化しても呼び出されることはない。

フックの引数

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

フックの関数による省略記法

第二引数に関数を渡すと、bindupdateにフックし、その処理を呼び出す。

Vue.directive('example', function(el, binding, vnode, oldVnode) {
    // bind と update で呼び出される処理
})

動画の再生をコントロールするサンプル

<div id="app">
<!--  動画の再生をコントロールする -->
  <!-- 動画1 -->
  <button v-on:click="video1=true">再生</button>
  <button v-on:click="video1=false">停止</button>
  <video src="movie1.mp4" v-video="video1"></video>
  <!-- 動画2 -->
  <button v-on:click="video2=true">再生</button>
  <button v-on:click="video2=false">停止</button>
  <video src="movie2.mp4" v-video="video2"></video>
</div>
var app = new Vue({
  el: '#app',
  data: {
    video1: false,
    video2: false
  },
  directives: {
    video(el, binding) {
      if (binding.value !== binding.oldValue) {
        binding.value ? el.play() : el.pause();
      }
    }
  }
})