Skip to content

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

Введение

Помимо стандартного набора директив, поставляемых в ядре (например, v-model или v-show), Vue также позволяет регистрировать собственные пользовательские директивы.

Мы представили две формы повторного использования кода во Vue: компоненты и composables. Компоненты являются основными строительными блоками, в то время как composables ориентированы на повторное использование логики, основанной на состоянии. Пользовательские директивы, с другой стороны, в основном предназначены для повторного использования логики, связанной с низкоуровневым доступом к DOM для простых элементов.

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

vue
<script setup>
// включение v-focus в шаблоне
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus />
</template>
js
const focus = {
  mounted: (el) => el.focus()
}

export default {
  directives: {
    // включение v-focus в шаблоне
    focus
  }
}
template
<input v-focus />

Если предположить, что вы не щёлкнули мышью в другом месте страницы, то приведенный выше элемент ввода должен быть автофокусирован. Эта директива более полезна, чем атрибут autofocus, поскольку работает не только при загрузке страницы — она также работает, когда элемент динамически вставляется Vue.

В <script setup>, любая переменная camelCase, начинающаяся с префикса v, может быть использована в качестве пользовательской директивы. В приведенном выше примере, vFocus может быть использован в шаблоне как v-focus.

Если не используется <script setup>, то пользовательские директивы могут быть зарегистрированы с помощью опции directives:

js
export default {
  setup() {
    /*...*/
  },
  directives: {
    // включение v-focus в шаблоне
    focus: {
      /* ... */
    }
  }
}

Как и компоненты, пользовательские директивы должны быть зарегистрированы, чтобы их можно было использовать в шаблонах. В приведенном примере мы используем локальную регистрацию с помощью опции directives.

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

js
const app = createApp({})

// сделать v-focus пригодным для использования во всех компонентах
app.directive('focus', {
  /* ... */
})

Совет

Пользовательские директивы следует использовать только в тех случаях, когда требуемая функциональность может быть достигнута только путём прямого манипулирования DOM. Предпочтительнее использовать декларативную шаблонизацию с помощью встроенных директив, таких как v-bind, поскольку они более эффективны и удобны для серверного рендеринга.

Хуки директив

Объект определения директивы может предоставлять несколько функций хуков (все они необязательны):

js
const myDirective = {
  // вызывается до применения атрибутов
  // связанного элемента или слушателей событий
  created(el, binding, vnode, prevVnode) {
    // подробнее об аргументах см. ниже
  },
  // вызывается непосредственно перед вставкой элемента в DOM.
  beforeMount(el, binding, vnode, prevVnode) {},
  // вызывается, когда родительский компонент связанного
  // элемента и все его дочерние элементы смонтированы.
  mounted(el, binding, vnode, prevVnode) {},
  // вызывается перед обновлением родительского компонента
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // вызывается после обновления родительского
  // компонента и всех его дочерних компонентов
  updated(el, binding, vnode, prevVnode) {},
  // вызывается перед размонтированием родительского компонента
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // вызывается при размонтировании родительского компонента
  unmounted(el, binding, vnode, prevVnode) {}
}

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

Эти аргументы передаются хукам директив:

  • 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: 'белый', text: 'привет!' }"></div>
js
app.directive('demo', (el, binding) => {
  console.log(binding.value.color) // => "белый"
  console.log(binding.value.text) // => "привет!"
})

Использование в компонентах

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

template
<MyComponent v-demo="test" />
template
<!-- шаблон MyComponent -->

<div> <!-- директива v-demo будет применена здесь -->
  <span>Содержание моего компонента</span>
</div>

Обратите внимание, что компоненты потенциально могут иметь более одного корневого узла. При применении к многокорневому компоненту директива проигнорируется и будет выдано предупреждение. В отличие от атрибутов, директивы не могут быть переданы другому элементу с помощью v-bind="$attrs". В общем случае не рекомендуется использовать пользовательские директивы для компонентов.

Пользовательские директивыУже загружено