Skip to content

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

Введение ​

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

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

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

vue
<script setup>
// включение v-highlight в шаблоне
const vHighlight = {
  mounted: (el) => {
    el.classList.add('is-highlight')
  }
}
</script>

<template>
  <p v-highlight>This sentence is important!</p>
</template>
js
const highlight = {
  mounted: (el) => el.classList.add('is-highlight')
}

export default {
  directives: {
    // включение v-highlight в шаблоне
    highlight
  }
}
template
<p v-highlight>This sentence is important!</p>

Это предложение важно!

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

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

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

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

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

js
const app = createApp({})

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

Глобальные пользовательские директивы можно типизировать, расширив интерфейс GlobalDirectives из vue.

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

Когда использовать пользовательские директивы ​

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

Распространенный пример этого — пользовательская директива v-focus, которая устанавливает фокус на элементе.

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!

Декларативное шаблонирование с использованием встроенных директив, таких как 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: { foo: true, 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". В общем случае не рекомендуется использовать пользовательские директивы для компонентов.

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