# Переименованы хуки пользовательских директив
кардинальное изменение

# Обзор

Были переименованы функции хуков директив, чтобы лучше соответствовать жизненному циклу компонента.

Кроме того, строка expression больше не передаётся как часть объекта binding.

# Синтаксис в 2.x

Во Vue 2 пользовательские директивы создавались с использованием хуков, указанных ниже, для обозначения жизненного цикла элемента, все они являлись опциональными:

  • bind - вызывается при привязке директивы к элементу. Вызывается только один раз.
  • inserted - вызывается после того, как элемент вставлен в родительский DOM.
  • update - вызывается при обновлениях элемента, но дочерние ещё не будут обновлены.
  • componentUpdated - вызывается после обновления компонента и его потомков.
  • unbind - вызывается после удаления директивы. Также вызывается только один раз.

Пример:

<p v-highlight="'yellow'">Выделить этот текст ярко-жёлтым</p>
1
Vue.directive('highlight', {
  bind(el, binding, vnode) {
    el.style.background = binding.value
  }
})
1
2
3
4
5

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

# Синтаксис в 3.x

Во Vue 3 теперь создан более согласованный API для пользовательских директив. Как можно увидеть, раньше он сильнее отличался от хуков жизненного цикла компонента, даже если привязывались к похожим событиям. Теперь они также унифицированы:

  • created - Новый! Вызывается перед применением атрибутов к элементу или слушателей событий.
  • bind → beforeMount
  • inserted → mounted
  • beforeUpdate: Новый! Вызывается перед обновлением самого элемента, подобно хуку жизненного цикла компонента.
  • update → Удалён! Слишком много сходств с updated, поэтому он избыточен. Вместо него используйте updated.
  • componentUpdated → updated
  • beforeUnmount: Новый! Подобно хуку жизненного цикла компонента, будет вызываться перед тем, как элемент будет размонтирован.
  • unbind -> unmounted

Финальное API выглядит так:

const MyDirective = {
  created(el, binding, vnode, prevVnode) {}, // новый
  beforeMount() {},
  mounted() {},
  beforeUpdate() {}, // новый
  updated() {},
  beforeUnmount() {}, // новый
  unmounted() {}
}
1
2
3
4
5
6
7
8
9

Используя обновлённое API, предыдущий пример теперь будет выглядеть так:

<p v-highlight="'yellow'">Выделить этот текст ярко-жёлтым</p>
1
const app = Vue.createApp({})

app.directive('highlight', {
  beforeMount(el, binding, vnode) {
    el.style.background = binding.value
  }
})
1
2
3
4
5
6
7

Теперь, когда хуки жизненного цикла пользовательских директив зеркально отражают хуки компонентов стало проще понимать когда их применять и проще запоминать!

# Крайний случай: Доступ к экземпляру компонента

Обычно рекомендуется сохранять директивы независимыми от экземпляра компонента, в котором они используются. Доступ к экземпляру внутри пользовательской директивы часто является признаком того, что директива скорее сама должна быть компонентом. Но бывают ситуации, когда это имеет смысл.

Во Vue 2 экземпляр компонента можно получить через аргумент vnode:

bind(el, binding, vnode) {
  const vm = vnode.context
}
1
2
3

Во Vue 3 этот экземпляр теперь является частью binding:

mounted(el, binding, vnode) {
  const vm = binding.instance
}
1
2
3

ВНИМАНИЕ

С появлением фрагментов компоненты теперь могут иметь больше одного корневого элемента. Если применить пользовательскую директиву к компоненту с несколькими корневыми элементами, то она будет проигнорирована и выведено предупреждение.

# Стратегия миграции

Флаг сборки для миграции: CUSTOM_DIR