カスタムディレクティブ
基本
コアで出荷されたディレクティブのデフォルトセットに加えて、カスタムディレクティブ (custom directive) を登録することができます。カスタムディレクティブは任意の DOM の振舞いへのマッピングデータを変更するためのメカニズムを提供します。
Vue.directive(id, definition)
メソッドで、directive id と definition object を続けて渡して、グローバルカスタムディレクティブに登録できます。それをコンポーネントの directives
オプションによってローカルカスタムディレクティブに登録することもできます。
フック関数
definition object はいくつかのフック関数(全て任意)を提供します:
bind: ディレクティブが初めて対象の要素にひも付いた時に一度だけ呼ばれます。
update: 初めの一度は bind の直後に初期値とともに呼ばれ、以降、バインディングされている値が変更される度に呼ばれます。引数には新しい値と以前の値が渡されます。
unbind: ディレクティブがひも付いている要素から取り除かれた時に一度だけ呼ばれます。
例
Vue.directive('my-directive', { |
一度登録された後は、以下のように Vue.js のテンプレート内で使用することができます (v-
の接頭辞を追加するのを忘れないでください):
<div v-my-directive="someValue"></div> |
update
関数のみが必要な場合は、definition object の代わりに関数を1つ渡すこともできます:
Vue.directive('my-directive', function (value) { |
ディレクティブインスタンスのプロパティ
全てのフック関数は実際に ディレクティブオブジェクト (directive object) にコピーされます。ディレクティブオブジェクトはフック関数の内側で this
のコンテキストとしてアクセスすることができます。このディレクティブオブジェクトはいくつかの便利なプロパティを持っています:
- el: ディレクティブがひも付く要素
- vm: このディレクティブを所有する ViewModel
- expression: 引数とフィルタ以外のバインディング式
- arg: 引数(もしある場合)
- name: 接頭辞 (prefix) 無しのディレクティブの名前
- modifiers: もしあれば、修飾子 (modifier) を含んでいるオブジェクト
- descriptor: 全体のディレクティブの解析結果を含むオブジェクト
- params: params 属性を含んでいるオブジェクト。以下で説明します
これらの全てのプロパティは読み込みのみ (read-only) で変更しないものとして扱わなくてはいけません。カスタムプロパティをディレクティブオブジェクトに追加することができますが、意図せずに既存の内部プロパティを上書きしないように注意が必要です。
いくつかのプロパティを使用したカスタムディレクティブの例:
<div id="demo" v-demo:hello.a.b="msg"></div> |
Vue.directive('demo', { |
結果
オブジェクトリテラル
あなたのディレクティブが複数の値を必要ならば、JavaScript オブジェクトリテラルも渡すことができます。ディレクティブは任意の妥当な JavaScript 式を取ることができるのを覚えておいてください:
<div v-demo="{ color: 'white', text: 'hello!' }"></div> |
Vue.directive('demo', function (value) { |
リテラル修飾子
ディレクティブがリテラル修飾子 (literal modifier) で使用されるとき、属性の値は、プレーンな文字列として解釈され、そして直接 update
メソッドに渡されます。update
メソッドはプレーンな文字列はリアクティブにできないため、一度だけ呼ばれます。
<div v-demo.literal="foo bar baz"> |
Vue.directive('demo', function (value) { |
エレメントディレクティブ
いくつのケースでは、属性としてよりむしろカスタム要素の形でディレクティブを使いたい場合があります。これは、Angular の “E” モードディレクティブの概念に非常に似ています。エレメントディレクティブ (element directive) は軽量な代替を本格的なコンポーネントとして提供します(ガイドの前半で説明されています)。カスタム要素をディレクティブのように登録できます:
Vue.elementDirective('my-directive', { |
この時、以下の代わりに:
<div v-my-directive></div> |
以下のように書くことができます:
<my-directive></my-directive> |
エレメントディレクティブは引数または式を受け付けることはできません。しかし、その振舞いを決定するために要素の属性を読み取ることはできます。
標準のディレクティブとの大きな違いは、エレメントディレクティブはターミナルで、Vue が一度エレメントディレクティブに遭遇したことを意味します。それは、要素とその子を残したまま、エレメントディレクティブそれ自体、要素とその子を操作することができるようになります。
高度なオプション
params
カスタムディレクティブは params
配列を提供でき、Vue コンパイラは自動的にディレクティブがバインドされた要素でこれらの属性を抽出します。例:
<div v-example a="hi"></div> |
Vue.directive('example', { |
この API は動的な属性もサポートします。this.params[key]
の値は自動的に最新に保ちます。加えて、値が変更されたときコールバックも指定できます:
<div v-example v-bind:a="someValue"></div> |
Vue.directive('example', { |
ディレクティブの params はJavaScript と HTML の間で同じキャメルケース⇔ケバブケースのマッピングに従うように、props と同様であることに注意してください。例えば、テンプレートで disable-effect
として param を使用するためには、JavaScript で disableEffect
としてそれにアクセスする必要があります。
deep
もしカスタムディレクティブでオブジェクトを扱いたい場合で、オブジェクトの内側のネストされたプロパティが変更された時に update
をトリガしたい場合は、ディレクティブの定義に deep: true
を渡す必要があります。
<div v-my-directive="obj"></div> |
Vue.directive('my-directive', { |
twoWay
あなたのディレクティブが Vue インスタンスにデータを書き戻す場合、twoWay: true
で渡す必要があります。このオプションは、ディレクティブ内部で this.set(value)
を使用することができます:
Vue.directive('example', { |
acceptStatement
acceptStatement: true
を渡すことでカスタムディレクティブが v-on
が行っているようなインラインステートメントを使用できるようになります:
<div v-my-directive="a++"></div> |
Vue.directive('my-directive', { |
ただし、テンプレート内のサイドエフェクトを避けるためにも、賢く使いましょう。
terminal
1.0.19+
Vue は DOM ツリーを再帰的に渡り歩くことによってテンプレートをコンパイルします。しかしながら、コンパイル処理において ターミナル なディレクティブに遭遇した場合、要素の子を渡り歩くのを停止します。ターミナルなディレクティブは要素とその子のコンパイルの仕事を引き継ぎます。例えば、 v-if
と v-for
は両方ともターミナルなディレクティブです。
カスタムディレクティブを実装することは高度なトピックで、そして Vue のコンパイルパイプラインの知識を必要としますが、ターミナルなディレクティブを実装することは可能です。terminal: true
を指定することによってカスタムターミナルディレクティブを指定することができます。また、おそらく部分的なコンパイルに対して Vue.Fragmentfactory
を使用する必要があります。ここでは、コンパイルとページ上の他の場所にコンテンツテンプレートを”注入”するカスタムターミナルディレクティブの例を示します:
var FragmentFactory = Vue.FragmentFactory |
<div id="modal"></div> |
カスタムターミナルディレクティブを実装したい場合、Vue 内部 のより良い理解を得るために、v-if
と v-for
のような組み込みのターミナルディレクティブのソースコードを読むことをお勧めします。
priority
ディレクティブには任意で優先度の数値 (デフォルトは 1000) を与えることができます。もし、優先度を指定されない場合は、デフォルトの優先度が使用されます。通常のディレクティブは 1000
、そしてターミナルなディレクティブは 2000
です。同じ要素上で高い優先度をもつディレクティブは他のディレクティブより早く処理されます。同じ優先度をもつディレクティブは要素上の属性のリストに出現する順番で処理されますが、ブラウザが異なる場合、一貫した順番になることは保証されません。
いくつかのビルトインディレクティブに関する優先度は API で確認できます。さらに フロー制御するディレクティブ v-if
と v-for
は、コンパイル処理の中で常に最も高い優先度を持ちます。