Какие директивы есть во Vue и как написать свою?

MiddleVue.js #vue #directives

Вопрос

Какие встроенные директивы есть во Vue, чем они отличаются друг от друга и как написать собственную директиву?

Короткий ответ

Директивы — это специальные атрибуты с префиксом v-, которые применяют реактивное поведение к DOM-элементу: v-if/v-show управляют видимостью, v-for рендерит списки, v-bind привязывает атрибуты, v-on — обработчики событий, v-model — двустороннюю привязку. Кастомная директива пишется как объект с хуками жизненного цикла (mounted, updated и т.д.) для прямой работы с DOM-элементом.

Подробный ответ

Встроенные директивы:

  • v-if / v-else-if / v-else — условный рендеринг: элемент полностью добавляется или удаляется из DOM. Подходит, когда условие меняется редко, т.к. переключение дороже.
  • v-show — переключает CSS-свойство display: none, элемент остаётся в DOM всегда. Дешевле при частом переключении, но дороже при первом рендере (элемент рендерится в любом случае).
  • v-for — рендерит список по массиву/объекту/диапазону: v-for="item in items" :key="item.id". Директиву всегда нужно сопровождать :key — уникальным идентификатором — чтобы Vue мог эффективно и корректно переиспользовать DOM-узлы при изменении списка.
  • v-bind (:) — реактивно привязывает атрибут/проп/класс/стиль к выражению: :src="url", :class="{ active }}", :disabled="isLoading".
  • v-on (@) — навешивает обработчик события: @click="handleClick", поддерживает модификаторы (@click.stop, @submit.prevent).
  • v-model — двусторонняя привязка на форменных элементах и компонентах (см. отдельный вопрос о формах).

Когда встроенных директив не хватает, можно написать кастомную директиву — объект с хуками, повторяющими жизненный цикл компонента: created, beforeMount, mounted, beforeUpdate, updated, beforeUnmount, unmounted. Каждый хук получает сам DOM-элемент (el) и объект binding со значением выражения (binding.value), аргументом (binding.arg) и модификаторами (binding.modifiers). Кастомные директивы нужны, когда требуется прямой императивный доступ к DOM-элементу — фокус, автосайзинг, интеграция со сторонней библиотекой, отслеживание клика вне элемента и т.п. — то, что нельзя выразить декларативно через шаблон.

Пример

<!-- директива v-focus: автофокус на элементе при монтировании -->
<script setup>
const vFocus = {
  mounted: (el) => el.focus()
}
</script>

<template>
  <input v-focus placeholder="Автофокус при появлении" />
</template>

Директива с параметром и модификатором — подсветка фона по значению и аргументу:

// directives/highlight.js
export const vHighlight = {
  mounted(el, binding) {
    // binding.value — переданное значение, binding.arg — v-highlight:color
    el.style.backgroundColor = binding.value || 'yellow'
  },
  updated(el, binding) {
    el.style.backgroundColor = binding.value || 'yellow'
  }
}
<script setup>
import { ref } from 'vue'
import { vHighlight } from './directives/highlight'

const color = ref('lightgreen')
</script>

<template>
  <p v-highlight="color">Текст с реактивной подсветкой фона</p>
</template>

Дополнительные вопросы

  • Почему v-for требует :key, и что произойдёт при его отсутствии или использовании индекса массива в качестве ключа?
  • В каком случае лучше выбрать v-show, а в каком v-if?
  • Можно ли использовать v-if и v-for на одном элементе, и почему так делать не рекомендуется?