логотип PurpleSchool
логотип PurpleSchool

Использование директив и их особенности на Vue с помощью define

Автор

Олег Марков

Введение

Директивы — это специальные расширения для разметки в Vue, которые позволяют напрямую управлять поведением DOM-элементов. Они делают шаблоны динамичными и адаптивными, предоставляя возможность «вливать» в HTML логику, не вынося её в отдельные компоненты. Во Vue уже встроен ряд стандартных директив (v-if, v-for, v-bind и другие), однако часто возникает задача написать свою — для особой работы с DOM или сторонними библиотеками.

В этой статье я расскажу, как использовать директивы в Vue, с упором на современные подходы (Vue 3) и работу через функцию define. Мы разберём разницу между стандартными и кастомными директивами, рассмотрим подробные примеры и узнаем, почему defineDirective или defineCustomElement делают работу с расширениями разметки проще и безопаснее. Отдельное внимание уделим жизненному циклу, возможным ошибкам, best practices и тонким местам при интеграции с Vue компонентами.


Кратко о директивах во Vue

Что такое директива?

Если говорить простыми словами, директива — это атрибут к элементу, начинающийся с префикса v-, который сообщает Vue обработать этот элемент особым образом. Пример — стандартная директива:

<!-- Директива v-show управляет видимостью -->
<div v-show="isVisible">Контент</div>

Vue смотрит на значение выражения после двоеточия, следит за ним, и по мере изменения приводит DOM к нужному виду. Но Vue позволяет создавать такие директивы самостоятельно — custom директивы.

Стандартные VS пользовательские директивы

Стандартные (v-if, v-for, v-bind, v-model и пр.) идут “из коробки”. Пользовательские директивы нужны, когда:

  • стандартных возможностей не хватает;
  • требуется сторонняя интеграция (например, подключить библиотеку drag-and-drop);
  • нужно реализовать специфическую логику управления DOM.

Создание пользовательских директив в Vue 3

Современный способ через функцию defineDirective

В Vue 3 удобнее создавать директивы через отдельную функцию. Обычно используют defineDirective, которая позволяет декларативно и удобно задать поведение.

Пример синтаксиса:

import { defineDirective } from 'vue'

const vFocus = defineDirective({
  mounted(el) {
    // Фокусируем элемент при вставке в DOM
    el.focus()
  }
})

В этом примере:

  • Функция defineDirective() принимает объект с методами жизненного цикла (о них далее).
  • Возвращается директива, которую вы сможете подключить глобально или локально в нужном компоненте.

Методы жизненного цикла пользовательских директив

У пользовательских директив свой набор методов:

  • created — срабатывает один раз, когда директива связывается с элементом.
  • beforeMount — вызывается непосредственно перед монтированием элемента.
  • mounted — когда элемент добавлен в DOM.
  • beforeUpdate — перед обновлением связанного значения.
  • updated — после обновления DOM.
  • beforeUnmount — перед удалением элемента.
  • unmounted — когда элемент удалён из DOM.

Давайте посмотрим, как эти методы применяются на практике.

Пример расширенной директивы с комментариями:

// Импортируем функцию для создания директивы
import { defineDirective } from 'vue'

const vLogger = defineDirective({
  mounted(el, binding) {
    // binding.value содержит значение, переданное директиве
    console.log('Монтируем элемент с параметром', binding.value)
  },
  updated(el, binding) {
    // Это сработает при изменении значения, с которым связана директива
    console.log('Обновляем элемент с новым значением', binding.value)
  },
  unmounted(el) {
    // Здесь можно обработать удаление из DOM
    console.log('Элемент удален из DOM')
  }
})

Теперь вы сможете подключить эту директиву и использовать, например, как:

<div v-logger="'Параметр для логирования'"></div>

Структура binding-объекта во Vue директивах

Объект binding, который попадает в методы директивы, содержит:

  • value — текущее значение;
  • oldValue — предыдущее значение (только для updated);
  • arg — аргумент (v-my-dir:foo="bar"foo тут аргумент);
  • modifiers — объект с флагами-модификаторами (v-fix.once);
  • instance — экземпляр компонента;
  • dir — объект самой директивы.

Иллюстрация binding-объекта

mounted(el, binding) {
  console.log(binding.value)      // значение: 'example'
  console.log(binding.arg)        // аргумент: 'myarg'
  console.log(binding.modifiers)  // модификаторы: { once: true }
}

Используем в шаблоне:

<div v-my-dir:myarg.once="'example'"></div>

Использование директив через define: Примеры и подходы

Локальное и глобальное подключение кастомных директив

Локальное подключение в компоненте

Вы можете импортировать директиву только в нужный компонент:

import { defineComponent } from 'vue'
import { vFocus } from './directives/vFocus.js'

export default defineComponent({
  directives: {
    focus: vFocus
  }
})

Теперь используете её как v-focus только в этом компоненте:

<input v-focus />

Глобальная регистрация директивы

Если директива нужна везде — регистрируйте её глобально при создании приложения:

import { createApp } from 'vue'
import vFocus from './directives/vFocus.js'
import App from './App.vue'

const app = createApp(App)

app.directive('focus', vFocus)

app.mount('#app')

Аргументы и модификаторы для директив

Аргументы позволяют передавать дополнительные параметры. Модификаторы — сконфигурировать поведение.

Пример с аргументом:

<button v-my-dir:primary="true"></button>

В JS:

mounted(el, binding) {
  if (binding.arg === 'primary') {
    // Например, добавляем синий фон главной кнопке
    el.style.background = 'blue'
  }
}

Пример с модификатором:

<input v-logger.once="hello" />

В директиве:

mounted(el, binding) {
  if (binding.modifiers.once) {
    // Поведение только при первой активации
    el.addEventListener('focus', () => alert('Welcome!'), { once: true })
  }
}

Модификаторы — это обычные булевы ключи в binding.modifiers.


Особенности работы директив в современных Vue проектах

Как работает defineDirective внутри

Функция defineDirective возвращает объект — не функцию! — с явным набором хуков (жизненных методов). Это важно, потому что старый синтаксис Vue 2 использовал функцию; сейчас лучше чётко разделять логику для каждой фазы.

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

Работа с рефами и реактивностью

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

import { ref } from 'vue'

const vAlert = defineDirective({
  updated(el, binding) {
    if (binding.value) {
      // binding.value — актуальное значение реактивной переменной
      el.style.background = 'yellow'
    }
  }
})

Взаимодействие директив и компонентов

Имейте в виду, что директива применяется только к корневому HTML-элементу компонента. Например:

<MyInput v-focus />

v-focus будет применён к корню шаблона MyInput, а не к вложенному <input>. Для “прокидывания” директив внутрь используйте сквозную передачу (v-bind="$attrs").


Лучшие практики и ограничения

Не дублируйте бизнес-логику в директивы

Директивы предназначены для работы с DOM. Не помещайте в них вычисления, API-запросы и общение с хранилищами, это снизит поддержку и тестируемость кода.

Используйте директивы для “низкоуровневых” задач

Если ваша задача связана с изменением поведения DOM (например, автофокус, drag-and-drop, подсказки, кастомные скроллбары), директива — отличное решение. Если вы строите композицию из данных и методов компонента, рассмотрите компонент вместо директивы.

Избегайте манипуляций вне жизненных методов

Работайте с DOM только во встроенных lifecycle-хуках (например, mounted, updated). Это гарантирует корректное взаимодействие c реактивной системой Vue.


Практические примеры — как реализовать популярные сценарии

Пример 1: Автофокусировка поля ввода

import { defineDirective } from 'vue'

export default defineDirective({
  mounted(el) {
    // Когда элемент появился — ставим фокус
    el.focus()
  }
})

Используйте в шаблоне:

<input v-focus />

Пример 2: Директива клика вне элемента (click-outside)

import { defineDirective } from 'vue'

export default defineDirective({
  beforeMount(el, binding) {
    // Функция-обработчик, вызывается при клике вне элемента
    el.handleClickOutside = event => {
      if (!el.contains(event.target)) {
        binding.value(event)
      }
    }
    document.addEventListener('click', el.handleClickOutside)
  },
  unmounted(el) {
    // Очищаем слушатель событий
    document.removeEventListener('click', el.handleClickOutside)
  }
})

Использование:

<div v-click-outside="methodToCallOnOutsideClick">
  Клик вне этого блока вызовет метод
</div>

Пример 3: Передача аргумента и модификатора

import { defineDirective } from 'vue'

export default defineDirective({
  mounted(el, binding) {
    if (binding.arg === 'red') {
      el.style.color = 'red'
    }
    if (binding.modifiers.bold) {
      el.style.fontWeight = 'bold'
    }
  }
})

В шаблоне:

<span v-my-dir:red.bold>Красный и жирный текст</span>

Интеграция с defineCustomElement — использование директив в кастомных элементах

Vue 3 поддерживает создание кастомных элементов через функцию defineCustomElement. Директивы легко встраиваются в такие компоненты:

import { defineCustomElement } from 'vue'
import vFocus from './directives/vFocus.js'

const MyInput = defineCustomElement({
  directives: { focus: vFocus },
  template: `<input v-focus />`
})

// Регистрируем элемент браузера
customElements.define('my-input', MyInput)

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

Тестирование директив лучше проводить интеграционно, монтируя компонент с Vue Test Utils и проверяя поведение DOM-элементов или вызовов функций:

import { mount } from '@vue/test-utils'
import vFocus from './directives/vFocus.js'

test('элемент получает фокус после монтирования', async () => {
  const wrapper = mount({
    template: '<input v-focus />',
    directives: { focus: vFocus }
  })
  expect(document.activeElement).toBe(wrapper.element)
})

Результаты работы директив всегда будут видны напрямую в DOM.


Заключение

Директивы Vue — мощный инструмент для работы с DOM, особенно когда нужно выйти за рамки стандартных возможностей и нужно гибко расширить поведение. Современный Vue призывает к чистому разнесению логики: компоненты отвечают за бизнес-взаимодействия, директивы — за прямую работу с элементами страницы. Функция defineDirective делает создание пользовательских директив проще, прозрачнее и безопаснее, а жизненный цикл позволяет контролировать поведение на всех этапах жизни элемента.

Применяйте директивы для низкоуровневых задач — автографика, кастомные слушатели, спецэффекты — и делайте ваш шаблон чище. Следуйте современным best practices, тестируйте директивы на монтировании/обновлении/удалении, пишите лаконичный и читаемый код. С этими инструментами вы сможете реализовать сложные сценарии без хаоса и магии!


Частозадаваемые технические вопросы по теме статьи и ответы на них

Вопрос 1. Как использовать одну и ту же директиву с разными параметрами на разных элементах?
Вы можете применять одну и ту же директиву с различными значениями, аргументами и модификаторами:
html <input v-my-dir:input.one="value1" /> <input v-my-dir:input.two="value2" /> В объекте binding будут разные поля arg и modifiers для каждого случая, их можно обрабатывать внутри директивы по необходимости.

Вопрос 2. Как гарантировать удаление слушателей событий/таймеров после демонтирования элемента?
Всегда добавляйте очистку (removeEventListener, clearInterval и проч.) в хуки unmounted или beforeUnmount вашей директивы. Это предотвратит утечки памяти и баги.

Вопрос 3. Как использовать директиву внутри v-for?
Директива корректно применяется к каждому элементу массива. Убедитесь, что у итерации (например, блока div v-for) есть уникальный :key.

Вопрос 4. Почему директива не срабатывает, если элемент скрыт через v-if?
Директива будет инициализирована только когда элемент отображён в DOM, так как v-if полностью убирает или добавляет элемент. После появления v-if директива выполнит свой mounted.

Вопрос 5. Можно ли использовать директиву одновременно с несколькими стандартными директивами на одном элементе?
Да, можно записать, например, <input v-model="value" v-focus v-example="param" /> — директивы будут работать независимо и не мешают друг другу, если не конфликтуют в логике работы с DOM.

Стрелочка влевоОсновы и применение директив в VueИспользование компонентов datepicker в Vue для выбора датСтрелочка вправо

Постройте личный план изучения Vue до уровня Middle — бесплатно!

Vue — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Vue 3 и Pinia

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий