Vue

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

Выпадающее меню должно закрываться по клику в любом месте за его пределами. Через обычные обработчики это означает повесить слушатель на document в onMounted, проверять, попал ли клик внутрь элемента, и не забыть снять слушатель в onUnmounted - и так в каждом компоненте с выпадашкой. Пользовательская директива v-click-outside сворачивает всю эту работу с DOM в одно слово в шаблоне. Директивы существуют ровно для того, что компонент выразить не может: прямое низкоуровневое поведение конкретного элемента.

  • v-focus: автофокус на поле при появлении, например на поиске в модалке
  • v-click-outside: закрытие выпадающего меню или поповера по клику снаружи
  • v-tooltip: показ подсказки при наведении без обёртки элемента в компонент
  • Интеграция jQuery-плагинов или библиотек анимации, требующих прямого доступа к DOM-узлу

Предварительные знания

  • Шаблонный синтаксис и привязка через v-bind
  • Понимание DOM-элемента и событий на нём
  • Знакомство с жизненным циклом на уровне идеи монтирования и размонтирования

Что такое пользовательская директива

Кроме встроенных директив (v-if, v-for, v-model) можно объявить свои. Пользовательская директива - это объект с функциями-хуками, каждая из которых получает сам DOM-элемент. Это даёт прямой низкоуровневый доступ к элементу, чего компоненты намеренно не предоставляют. Локальную директиву объявляют в script setup переменной с префиксом v, и она доступна в шаблоне того же компонента.

Имя переменной обязано начинаться с v и далее идти в camelCase: vFocus в коде превращается в v-focus в шаблоне. Глобальную директиву регистрируют через app.directive('focus', {...}), и тогда она доступна во всех компонентах приложения.

Директивы стоит держать как последнее средство для прямого DOM-доступа. Если задачу можно выразить компонентом или composable, обычно так понятнее: директива работает в обход декларативной модели и её сложнее отлаживать.

Что отличает пользовательскую директиву от компонента?

Хуки директивы

Директива объявляет хуки, повторяющие жизненный цикл элемента. mounted вызывается, когда элемент вставлен в DOM. updated - после обновления содержащего его компонента. beforeUnmount - перед удалением элемента, и это место для очистки: снять слушатели, остановить таймеры. Есть также created и beforeMount для ранних стадий, но на практике чаще нужны mounted и beforeUnmount.

ХукКогда вызываетсяТипичная задача
mountedЭлемент вставлен в DOMПодписка, фокус, init библиотеки
updatedКомпонент обновилсяРеакция на смену значения директивы
beforeUnmountПеред удалением элементаОчистка: снять слушатели, остановить таймеры

Парность mounted и beforeUnmount - тот же принцип, что у onMounted и onUnmounted в компоненте. Подписку, поставленную в mounted, обязательно снимают в beforeUnmount, иначе слушатель на document утечёт.

В каком хуке директивы снимают слушатель события, добавленный в mounted?

Аргументы, модификаторы и значение

Директива принимает данные тремя способами, и все они приходят во втором параметре хука - объекте binding. Значение передаётся присваиванием (v-pin="200") и доступно как binding.value. Аргумент после двоеточия (v-pin:top) приходит в binding.arg. Модификаторы после точки (v-pin.smooth) собираются в binding.modifiers как объект флагов.

  • binding.value - значение справа от знака равенства (здесь 80)
  • binding.oldValue - предыдущее значение, доступно в хуке updated
  • binding.arg - аргумент после двоеточия (здесь 'top')
  • binding.modifiers - объект флагов после точек (здесь { smooth: true })

Аргумент может быть динамическим: v-pin:[side]="80", где side - реактивная переменная. Это позволяет менять поведение директивы на лету, не переписывая шаблон под каждый случай.

В записи v-pin:top.smooth="80" что попадёт в binding.arg, binding.modifiers и binding.value?

Когда директива, а когда компонент

Директива оправдана, когда нужно низкоуровневое поведение конкретного DOM-элемента: фокус, клик вне, наблюдатель пересечений, интеграция сторонней библиотеки по узлу. Если же задача про разметку, состояние и логику данных, почти всегда уместнее компонент или composable. Директива работает в обход декларативной модели, поэтому злоупотребление ею делает код труднее для понимания.

  • Директива — Прямое поведение одного DOM-элемента: фокус, click-outside, tooltip, lazy-load изображения
  • Компонент — Разметка, состояние, переиспользуемый блок интерфейса с собственным шаблоном
ЗадачаИнструмент
Автофокус на поле при появленииДиректива v-focus
Закрытие по клику снаружиДиректива v-click-outside
Карточка с заголовком и слотамиКомпонент
Переиспользуемая логика загрузки данныхComposable

Проверочный вопрос: нужен ли прямой доступ к DOM-узлу. Если да и это про поведение элемента - директива. Если задача описывается через состояние и разметку - компонент. Если это переиспользуемая логика без привязки к конкретному узлу - composable.

Для какой из задач пользовательская директива - более подходящий инструмент, чем компонент?

Связь с другими темами

Директивы дают низкоуровневый доступ к DOM там, где компонента и composable мало:

  • Хуки жизненного цикла — Хуки директивы повторяют идею mounted и beforeUnmount, но привязаны к одному элементу
  • Composables — Оба приёма инкапсулируют логику с очисткой; выбор зависит от того, нужен ли доступ к DOM-узлу

Итог

  • Пользовательская директива - объект с хуками, получающими сам DOM-элемент для прямого управления им
  • Основные хуки: mounted (элемент в DOM), updated (после обновления), beforeUnmount (перед удалением, для очистки)
  • Аргумент передаётся через двоеточие (v-pin:top), модификаторы через точку (v-pin.smooth), значение через присваивание
  • Все данные приходят в объект binding: value, oldValue, arg, modifiers
  • Директивы нужны для низкоуровневого поведения DOM; для логики с состоянием обычно лучше компонент или composable

Связанные уроки

  • vue-11-lifecycle — Хуки директивы (mounted, beforeUnmount) повторяют идею жизненного цикла, но для отдельного DOM-элемента
  • vue-12-composables — И директива, и composable инкапсулируют логику с очисткой, но директива работает на уровне DOM-элемента
Пользовательские директивы

0

1

Войти