Пользовательские директивы
Введение
Помимо стандартного набора директив, поставляемых в ядре (например, v-model
или v-show
), Vue также позволяет регистрировать собственные директивы.
Мы представили две формы повторного использования кода во Vue: компоненты и композаблы. Компоненты являются основными строительными блоками, в то время как композаблы сосредоточены на повторном использовании логики, основанной на состоянии. Пользовательские директивы, с другой стороны, в основном предназначены для повторного использования логики, которая включает низкоуровневый доступ к DOM для простых элементов.
Пользовательская директива определяется как объект, содержащий хуки жизненного цикла, аналогичные тем, что используются в компоненте. Эти хуки получают элемент, к которому привязана директива. Вот пример директивы, которая добавляет класс к элементу, когда он вставляется в DOM с помощью Vue:
vue
<script setup>
// включает v-highlight в шаблонах
const vHighlight = {
mounted: (el) => {
el.classList.add('is-highlight')
}
}
</script>
<template>
<p v-highlight>Это предложение очень важно!</p>
</template>
Это предложение очень важно!
В <script setup>
любая переменная в «верблюжьем» регистре, начинающаяся с префикса v
, может быть использована в качестве пользовательской директивы. В приведённом выше примере vHighlight
можно использовать в шаблоне как v-highlight
.
Если не используется <script setup>
, пользовательские директивы могут быть зарегистрированы с помощью опции directives
:
js
export default {
setup() {
/*...*/
},
directives: {
// включает v-highlight в шаблонах
highlight: {
/* ... */
}
}
}
Также часто встречается глобальная регистрация пользовательских директив на уровне приложения:
js
const app = createApp({})
// делаем v-highlight пригодным для использования во всех компонентах
app.directive('highlight', {
/* ... */
})
Когда использовать пользовательские директивы
Пользовательские директивы следует использовать только в тех случаях, когда требуемая функциональность может быть достигнута только путём прямого манипулирования DOM.
Частым примером этого является пользовательская директива v-focus
, которая переводит элемент в фокус.
vue
<script setup>
// включает v-focus в шаблонах
const vFocus = {
mounted: (el) => el.focus()
}
</script>
<template>
<input v-focus />
</template>
Эта директива более полезна, чем атрибут autofocus
, потому что она работает не только при загрузке страницы, но и когда элемент динамически вставляется Vue!
Декларативный шаблонизатор со встроенными директивами, такими как v-bind
, рекомендуется использовать, когда это возможно, поскольку они более эффективны и удобны для серверного рендеринга.
Хуки директив
Объект определения директивы может предоставлять несколько функций хука (все они необязательны):
js
const myDirective = {
// вызывается до применения атрибутов связанного
// элемента или слушателей событий
created(el, binding, vnode) {
// подробнее об аргументах см. ниже
},
// вызывается непосредственно перед вставкой элемента в DOM.
beforeMount(el, binding, vnode) {},
// вызывается, когда родительский компонент связанного элемента
// и все его дочерние элементы смонтированы.
mounted(el, binding, vnode) {},
// вызывается перед обновлением родительского компонента
beforeUpdate(el, binding, vnode, prevVnode) {},
// вызывается после того, как родительский компонент
// и все его дочерние компоненты обновятся
updated(el, binding, vnode, prevVnode) {},
// вызывается перед размонтированием родительского компонента
beforeUnmount(el, binding, vnode) {},
// вызывается, когда родительский компонент размонтирован
unmounted(el, binding, vnode) {}
}
Аргументы хуков
Эти аргументы передаются хукам директив:
el
: элемент, к которому привязана директива. Это можно использовать для прямого манипулирования DOM.binding
: объект, содержащий следующие свойства.value
: Значение, передаваемое директиве. Например, вv-my-directive="1 + 1"
значение будет2
.oldValue
: Предыдущее значение, доступное только вbeforeUpdate
иupdated
. Оно доступно независимо от того, изменилось значение или нет.arg
: Аргумент, переданный директиве, если таковой имеется. Например, вv-my-directive:foo
аргументом будет"foo"
.modifiers
: Объект, содержащий модификаторы, если таковые имеются. Например, вv-my-directive.foo.bar
объект модификаторов будет иметь вид{ foo: true, bar: true }
.instance
: Экземпляр компонента, в котором используется директива.dir
: объект определения директивы.
vnode
: базовый узел VNode, представляющий связанный элемент.prevVnode
: VNode, представляющий связанный элемент из предыдущего рендера. Доступно только в хукахbeforeUpdate
иupdated
.
В качестве примера рассмотрим использование следующей директивы:
template
<div v-example:foo.bar="baz">
Аргумент binding
будет представлять собой объект в форме:
js
{
arg: 'foo',
modifiers: { bar: true },
value: /* значение `baz` */,
oldValue: /* значение `baz` из предыдущего обновления */
}
Подобно встроенным директивам, аргументы пользовательских директив могут быть динамическими. Например:
template
<div v-example:[arg]="value"></div>
Здесь аргумент директивы будет реактивно обновляться на основе свойства arg
в состоянии нашего компонента.
Примечание
Кроме el
, вы должны рассматривать эти аргументы как предназначенные только для чтения и никогда не изменять их. Если вам нужно обмениваться информацией между хуками, рекомендуется делать это через dataset элемента.
Директива в виде функции
Обычно пользовательская директива имеет одинаковое поведение для mounted
и updated
и не нуждается в других хуках. В таких случаях мы можем определить директиву как функцию:
template
<div v-color="color"></div>
js
app.directive('color', (el, binding) => {
// это будет вызвано для `mounted` и `updated`.
el.style.color = binding.value
})
Литералы объектов
Если вашей директиве требуется несколько значений, вы также можете передать объектный литерал JavaScript. Помните, что директивы могут принимать любое правильное выражение JavaScript.
template
<div v-demo="{ color: 'white', text: 'привет!' }"></div>
js
app.directive('demo', (el, binding) => {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "привет!"
})
Использование в компонентах
Не рекомендуется
Использование пользовательских директив для компонентов не рекомендуется. При наличии у компонента нескольких корневых узлов может возникнуть непредвиденное поведение.
При использовании в компонентах пользовательские директивы всегда будут применяться к корневому узлу компонента, подобно обычным атрибутам.
template
<MyComponent v-demo="test" />
template
<!-- шаблон MyComponent -->
<div> <!-- Здесь будет применена директива v-demo -->
<span>Содержание компонента</span>
</div>
Обратите внимание, что компоненты потенциально могут иметь более одного корневого узла. При применении к мультикорневому компоненту директива будет проигнорирована, и будет выдано предупреждение. В отличие от атрибутов, директивы не могут быть переданы другому элементу с помощью v-bind="$attrs"
.