自定義指令
除了核心功能默認(rèn)內(nèi)置的指令 (v-model 和 v-show),Vue 也允許注冊自定義指令。注意,在 Vue2.0 中,代碼復(fù)用和抽象的主要形式是組件。
目錄
簡介
除了核心功能默認(rèn)內(nèi)置的指令 (v-model
和 v-show
),Vue 也允許注冊自定義指令。注意,在 Vue2.0 中,代碼復(fù)用和抽象的主要形式是組件。然而,有的情況下,你仍然需要對普通 DOM 元素進(jìn)行底層操作,這時候就會用到自定義指令。舉個聚焦輸入框的例子,如下:
當(dāng)頁面加載時,該元素將獲得焦點(diǎn) (注意:autofocus
在移動版 Safari 上不工作)。事實(shí)上,只要你在打開這個頁面后還沒點(diǎn)擊過任何內(nèi)容,這個輸入框就應(yīng)當(dāng)還是處于聚焦?fàn)顟B(tài)。現(xiàn)在讓我們用指令來實(shí)現(xiàn)這個功能:
// 注冊一個全局自定義指令 `v-focus` Vue.directive('focus', { // 當(dāng)被綁定的元素插入到 DOM 中時…… inserted: function (el) { // 聚焦元素 el.focus() } })
如果想注冊局部指令,組件中也接受一個 directives
的選項(xiàng):
directives: { focus: { // 指令的定義 inserted: function (el) { el.focus() } } }
然后你可以在模板中任何元素上使用新的 v-focus
屬性,如下:
<input v-focus>
鉤子函數(shù)
一個指令定義對象可以提供如下幾個鉤子函數(shù) (均為可選):
bind
:只調(diào)用一次,指令第一次綁定到元素時調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置。inserted
:被綁定元素插入父節(jié)點(diǎn)時調(diào)用 (僅保證父節(jié)點(diǎn)存在,但不一定已被插入文檔中)。update
:所在組件的 VNode 更新時調(diào)用,但是可能發(fā)生在其子 VNode 更新之前。指令的值可能發(fā)生了改變,也可能沒有。但是你可以通過比較更新前后的值來忽略不必要的模板更新 (詳細(xì)的鉤子函數(shù)參數(shù)見下)。
componentUpdated
:指令所在組件的 VNode 及其子 VNode 全部更新后調(diào)用。unbind
:只調(diào)用一次,指令與元素解綁時調(diào)用。
接下來我們來看一下鉤子函數(shù)的參數(shù) (即 el
、binding
、vnode
和 oldVnode
)。
鉤子函數(shù)參數(shù)
指令鉤子函數(shù)會被傳入以下參數(shù):
el
:指令所綁定的元素,可以用來直接操作 DOM 。binding
:一個對象,包含以下屬性:name
:指令名,不包括 v- 前綴。value
:指令的綁定值,例如:v-my-directive="1 + 1"
中,綁定值為2
。oldValue
:指令綁定的前一個值,僅在update
和componentUpdated
鉤子中可用。無論值是否改變都可用。expression
:字符串形式的指令表達(dá)式。例如v-my-directive="1 + 1"
中,表達(dá)式為"1 + 1"
。arg
:傳給指令的參數(shù),可選。例如v-my-directive:foo
中,參數(shù)為"foo"
。modifiers
:一個包含修飾符的對象。例如:v-my-directive.foo.bar
中,修飾符對象為{ foo: true, bar: true }
。
vnode
:Vue 編譯生成的虛擬節(jié)點(diǎn)。移步 VNode API 來了解更多詳情。oldVnode
:上一個虛擬節(jié)點(diǎn),僅在update
和componentUpdated
鉤子中可用。
除了
el
之外,其它參數(shù)都應(yīng)該是只讀的,切勿進(jìn)行修改。如果需要在鉤子之間共享數(shù)據(jù),建議通過元素的dataset
來進(jìn)行。
這是一個使用了這些屬性的自定義鉤子樣例:
<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>
Vue.directive('demo', { bind: function (el, binding, vnode) { var s = JSON.stringify el.innerHTML = 'name: ' + s(binding.name) + '<br>' + 'value: ' + s(binding.value) + '<br>' + 'expression: ' + s(binding.expression) + '<br>' + 'argument: ' + s(binding.arg) + '<br>' + 'modifiers: ' + s(binding.modifiers) + '<br>' + 'vnode keys: ' + Object.keys(vnode).join(', ') } }) new Vue({ el: '#hook-arguments-example', data: { message: 'hello!' } })
動態(tài)指令參數(shù)
指令的參數(shù)可以是動態(tài)的。例如,在 v-mydirective:[argument]="value"
中,argument
參數(shù)可以根據(jù)組件實(shí)例數(shù)據(jù)進(jìn)行更新!這使得自定義指令可以在應(yīng)用中被靈活使用。
例如你想要創(chuàng)建一個自定義指令,用來通過固定布局將元素固定在頁面上。我們可以像這樣創(chuàng)建一個通過指令值來更新豎直位置像素值的自定義指令:
<div id="baseexample"> <p>Scroll down the page</p> <p v-pin="200">Stick me 200px from the top of the page</p> </div>
Vue.directive('pin', { bind: function (el, binding, vnode) { el.style.position = 'fixed' el.style.top = binding.value + 'px' } }) new Vue({ el: '#baseexample' })
這會把該元素固定在距離頁面頂部 200 像素的位置。但如果場景是我們需要把元素固定在左側(cè)而不是頂部又該怎么辦呢?這時使用動態(tài)參數(shù)就可以非常方便地根據(jù)每個組件實(shí)例來進(jìn)行更新。
<div id="dynamicexample"> <h3>Scroll down inside this section ↓</h3> <p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.</p> </div>
Vue.directive('pin', { bind: function (el, binding, vnode) { el.style.position = 'fixed' var s = (binding.arg == 'left' ? 'left' : 'top') el.style[s] = binding.value + 'px' } }) new Vue({ el: '#dynamicexample', data: function () { return { direction: 'left' } } })
結(jié)果:
這樣這個自定義指令現(xiàn)在的靈活性就足以支持一些不同的用例了。
函數(shù)簡寫
在很多時候,你可能想在 bind
和 update
時觸發(fā)相同行為,而不關(guān)心其它的鉤子。比如這樣寫:
Vue.directive('color-swatch', function (el, binding) { el.style.backgroundColor = binding.value })
對象字面量
如果指令需要多個值,可以傳入一個 JavaScript 對象字面量。記住,指令函數(shù)能夠接受所有合法的 JavaScript 表達(dá)式。
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
Vue.directive('demo', function (el, binding) { console.log(binding.value.color) // => "white" console.log(binding.value.text) // => "hello!" })