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', {
  /* ... */
})

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

Пользовательские директивы следует использовать только тогда, когда желаемая функциональность может быть достигнута только через прямое манипулирование 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: { 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". В общем случае не рекомендуется использовать пользовательские директивы для компонентов.

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