Перейти к русской документации для v3.x.

Эта документация для версий v2.x и ранее. Для v3.x, документация на русском здесь.

Пользовательские директивы

Введение

Помимо встроенных директив (таких как v-model и v-show), Vue позволяет использовать ваши собственные. При этом важно понимать, что основным механизмом создания повторно используемого кода во Vue 2.0 всё-таки являются компоненты. Тем не менее, для выполнения низкоуровневых операций с DOM пользовательские директивы могут очень пригодиться. В качестве примера реализуем фокус на элементе input:

После загрузки страницы этот элемент получает фокус ввода (примечание: autofocus не работает на мобильном Safari). Если вы никуда не кликнули с момента открытия этой главы руководства, фокус ввода и сейчас должен быть на этом элементе. Рассмотрим директиву подробнее:

// Регистрируем глобальную пользовательскую директиву `v-focus`
Vue.directive('focus', {
// Когда привязанный элемент вставлен в DOM...
inserted: function (el) {
// Переключаем фокус на элемент
el.focus()
}
})

Чтобы зарегистрировать директиву локально, можно передать опцию directives при определении компонента:

directives: {
focus: {
// определение директивы
inserted: function (el) {
el.focus()
}
}
}

Теперь в шаблонах можно использовать новый атрибут v-focus:

<input v-focus>

Хуки

Для жизненного цикла директивы можно указать следующие хуки (все они опциональны):

Подробнее мы обсудим VNodes позднее, когда будем разбирать Render-функции.

В следующем разделе мы рассмотрим аргументы, передаваемые в эти хуки (а именно: el, binding, vnode и oldVnode).

Аргументы хуков

В хуки передаются следующие параметры:

Все аргументы, кроме el, следует понимать как только для чтения и никогда не изменять их. В случае необходимости передать информацию между хуками рекомендуем воспользоваться dataset.

Пример пользовательской директивы, использующей некоторые из описанных возможностей:

<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: 'привет!'
}
})

Динамические аргументы директивы

Аргументы директивы могут быть динамическими. Например, для v-mydirective:[argument]="value", argument может обновляться в зависимости от свойства данных экземпляра компонента! Это позволит сделать пользовательские директивы более гибкими при использовании в приложении.

Допустим, необходимо создать собственную директиву, которая позволит установить элементы на странице с помощью фиксированного позиционирования. Можно создать пользовательскую директиву, где значение определяет вертикальное положение в пикселях, например так:

<div id="baseexample">
<p>Прокрутите страницу вниз</p>
<p v-pin="200">Элемент зафиксирован в 200px от начала страницы</p>
</div>
Vue.directive('pin', {
bind: function (el, binding, vnode) {
el.style.position = 'fixed'
el.style.top = binding.value + 'px'
}
})

new Vue({
el: '#baseexample'
})

Это закрепит элемент в 200px от начала страницы. Но что если возникнет случай, когда необходимо закрепить элемент слева, а не сверху? Для этого пригодится динамический аргумент директивы, который можно определить для каждого экземпляра компонента:

<div id="dynamicexample">
<h3>Прокрутите страницу вниз</h3>
<p v-pin:[direction]="200">Элемент зафиксирован в 200px слева страницы.</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'
}
}
})

Результат:

Теперь пользовательская директива достаточно гибкая для использования в нескольких различных случаях.

Сокращённая запись

Часто нам может потребоваться одинаковое поведение на bind и update, а остальные хуки не нужны. В таком случае можно использовать сокращённую запись:

Vue.directive('color-switch', function (el, binding) {
el.style.backgroundColor = binding.value
})

Передача объекта данных в директиву

В случае, если директива должна принимать несколько параметров, можно указать объект JavaScript — годится любое валидное выражение, помните?

<div v-demo="{ color: 'белый', text: 'привет!' }"></div>
Vue.directive('demo', function (el, binding) {
console.log(binding.value.color) // => "белый"
console.log(binding.value.text) // => "привет!"
})