Vue: пользовательские директивы Печать
Добавил(а) microsin   

В шаблонах Vue используют предопределенные директивы [1], управляющие отображением страницы. Но также у пользователя есть возможность создать собственные директивы.

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

Имя объекта для директивы должно начинаться на букву v, например:

< script setup>
import { ref, onMounted } from 'vue'
...
const vFocus = {
mounted: (el) => el.focus() }; < /script>

Тогда в шаблоне эта директива будет доступна под именем v-focus.

Пользовательские директивы Vue представляют собой инструмент для повторного использования логики низкоуровневого доступа к DOM (Document Object Model), который применяется, когда декларативных возможностей компонентов и composables недостаточно.

Ниже — структурированный обзор того, как они работают в Vue 3 (с ключевыми отличиями от Vue 2).

[1. Назначение и когда использовать]

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

Когда нужны:

- Автофокус (el.focus()).
- Интеграция сторонних библиотек (drag & drop, кастомные скроллбары).
- Манипуляции с атрибутами/стилями при появлении элемента.
- Ленивая загрузка изображений (IntersectionObserver).
- UI-фичи: закрепление элемента (`v-pin`).

Когда НЕ нужны:

- Вся бизнес-логика, работа с API, хранилища (Pinia/Vuex) — это задача компонентов или composables.

[2. Структура и жизненные хуки (Vue 3)]

Директива — это объект с хуками, аналогичными хукам компонента. Все хуки опциональны.

hook Момент вызова
created До применения атрибутов/слушателей
beforeMount Перед вставкой элемента в DOM
mounted Элемент в DOM (самый частый хук)
beforeUpdate Перед обновлением родителя
updated После обновления родителя и детей
beforeUnmount Перед удалением элемента
unmounted Элемент удалён из DOM (чистим слушатели)

Vue 2 vs Vue 3 — названия хуков изменились:

- bind → mounted
- inserted → удалён
- update → updated
- componentUpdated → удалён
- unbind → unmounted 

[3. Аргументы хуков (объект binding)]

В каждый хук приходит 4 аргумента:  

mounted(el, binding, vnode, prevVnode)

Объект binding — самое важное:

{  value:        // переданное значение (v-dir="1" → 1)
oldValue: // предыдущее (только update/updated)
arg: // аргумент (v-dir:foo → 'foo')
modifiers: // модификаторы (v-dir.once → { once: true })
instance: // экземпляр компонента
dir: // объект самой директивы }

Динамический аргумент — реактивно обновляется:

< div v-pin:[direction]="200">< /div>  < !-- direction может меняться -->

[4. Создание и регистрация]

Локально (в компоненте):

Composition API (< script setup>) — автоматически, если переменная с префиксом `v`:

< script setup>
const vFocus = { mounted: (el) => el.focus() } < /script>
< template> < input v-focus /> < /template>

Options API — через опцию `directives`:

export default {
directives: {
focus: { mounted: (el) => el.focus() }
} }

Глобально:

import { createApp } from 'vue'
const app = createApp(App) app.directive('focus', { mounted: (el) => el.focus() })

Доступна во всех компонентах без импорта.

[5. Синтаксический сахар]

Если логика одинакова для mounted и updated, то директива может быть функцией:

app.directive('color', (el, binding) => {
el.style.color = binding.value })

Передача объекта параметров:

< div v-demo="{ color: 'white', text: 'hello' }">< /div>

[6. Особенности работы с компонентами]

- На однокорневом компоненте — директива применяется к корневому узлу.
- На многокорневом (fragment) — игнорируется, будет warning.
- Нельзя прокинуть через v-bind="$attrs" (в отличие от атрибутов).

Не рекомендуется использовать пользовательские директивы на компонентах.

[7. Практические примеры из реальной разработки]

Сценарий Суть реализации
Автофокус с задержкой setTimeout(() => el.focus(), binding.value || 0)
Ленивая загрузка IntersectionObserver → подмена el.src
Закрепление (pin) el.style.position = fixed, top/left из binding.arg
Кнопка по ролям el.disabled = true если роль не совпадает

[8. Best Practices]

1. Чистота: директива — только DOM. API, сторы, вычисления — в компонент.
2. Очистка: если навесили addEventListener в mounted — снимите в unmounted.
3. Простота: если директива сложнее 10 строк — возможно нужен компонент.
4. Не злоупотреблять: встроенные v-bind, v-show, v-if почти всегда лучше кастомных.
5. Чтение: объект binding (кроме el) — readonly. Обмен данными между хуками — через el.dataset.

Если вы переходите с Vue 2: самое важное изменение — имена хуков и отказ от синтаксиса "одной функции по умолчанию". Vue 3 предлагает чёткий объект с именованными методами жизненного цикла.

[Ссылки]

1. Директивы шаблона Vue.