Пользовательские директивы
Введение
Помимо стандартного набора директив, поставляемых в ядре (например, 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', {
/* ... */
})Возможно типизировать глобальные пользовательские директивы, расширяя интерфейс ComponentCustomProperties из vue.
Подробнее: Типизация глобальных пользовательских директив
Когда использовать пользовательские директивы
Пользовательские директивы следует использовать только в тех случаях, когда требуемая функциональность может быть достигнута только путём прямого манипулирования 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".