Vue.js предоставляет богатый набор директив для упрощения работы разработчиков. Помимо встроенных директив, Vue.js также поддерживает директивы Custom, которые позволяют разработчикам расширять библиотеку директив Vue.js в соответствии со своими потребностями. Vue.js 3.x имеет некоторые улучшения в директиве Custom по сравнению с Vue.js 2.x. В этой статье рассказывается, как использовать директиву Custom в Vue.js 3.x.

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

1. Введение

В Vue.js директивы — это специальные свойства с префиксом v-. Что он делает, так это немедленно выполняет некоторое действие, когда элемент, к которому он привязан, вставляется в DOM. В Vue.js есть много встроенных директив, например:

  • v-model: создает двунаправленную привязку данных к элементам формы;
  • v-show: переключает отображение свойства CSS элемента на основе значения true или false выражения;
  • v-if: визуализирует или уничтожает элемент на основе true или false значения выражения;
  • v-for: отображает список на основе массива.

Эти директивы позволяют нам более декларативно манипулировать DOM, скрывая сложную логику манипулирования DOM. Помимо встроенных директив, Vue.js также позволяет нам зарегистрировать Пользовательскую директиву. Пользовательская директива позволяет нам применять настраиваемое поведение к визуализируемым элементам DOM.

2. Основное использование

Возьмем для примера директиву Global Custom, она регистрируется глобальным методом app.directive(name, options) и применяется в шаблоне с префиксом v-. Метод directive() принимает два параметра.

  • name: название директивы, например. focus;
  • options: объект конфигурации директивы, который содержит функцию-ловушку для директивы.

Ниже приведен пример пользовательской директивы v-focus, которая создается путем предварительного создания директивы v-focus:

const app = createApp({});
app.directive("focus", {
  // When the bound element is inserted into the DOM ......
  mounted(el) {
    // Focusing elements
    el.focus();
  },
});

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

<input v-focus />

Когда поле ввода монтируется в DOM, оно автоматически получает фокус.

Объект определения директивы может предоставлять несколько функций ловушек (все необязательные):

const myDirective = {
  // called before bound element's attributes
  // or event listeners are applied
  created(el, binding, vnode, prevVnode) {
    // see below for details on arguments
  },
  // called right before the element is inserted into the DOM.
  beforeMount(el, binding, vnode, prevVnode) {},
  // called when the bound element's parent component
  // and all its children are mounted.
  mounted(el, binding, vnode, prevVnode) {},
  // called before the parent component is updated
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // called after the parent component and
  // all of its children have updated
  updated(el, binding, vnode, prevVnode) {},
  // called before the parent component is unmounted
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // called when the parent component is unmounted
  unmounted(el, binding, vnode, prevVnode) {},
};

Перехватчикам директив передаются следующие аргументы:

  • el: элемент, к которому привязана директива. Это можно использовать для прямого управления DOM.
  • binding:Объект, содержащий атрибуты value, oldValue, arg, modifiers, instance и dir.
  • vnode: базовый VNode, представляющий связанный элемент.
  • prevNode: VNode, представляющий связанный элемент из предыдущего рендеринга. Доступно только в крюках beforeUpdate и updated.

Подробное описание параметров см. в документе Аргументы хука.

🗂️ Классификация

1. Классификация по способу регистрации Директивы

Пользовательские директивы можно классифицировать по методу регистрации: глобальные директивы и локальные директивы.

  • Глобальная директива

Глобально зарегистрированные директивы могут использоваться в любом компоненте приложения и обычно регистрируются в экземпляре Vue app через directive():

const app = createApp({});
app.directive("focus", {
  // When the bound element is inserted into the DOM ......
  mounted(el) {
    // Focusing elements
    el.focus();
  },
});
  • Локальная директива

Локально зарегистрированные директивы можно использовать только в компоненте, с которым они зарегистрированы в объекте конфигурации компонента:

const Component = defineComponent({
  directives: {
    focus: {
      mounted(el) {
        el.focus();
      },
    },
  },
  render() {
    const { directives } = this.$options;
    return [withDirectives(h("input"), [[directives.focus]])];
  },
});

2. Классификация по методу реализации Директивы

Пользовательскую директиву можно классифицировать по методу реализации: директива объекта и директива функции.

  • Директива объекта

Object Directive реализованы как объекты и предоставляют дополнительные опции и методы жизненного цикла:

const app = createApp({});
app.directive("focus", {
  // When the bound element is inserted into the DOM ......
  mounted(el) {
    // Focusing elements
    el.focus();
  },
});

В исходном коде внутри определения типа интерфейса:

export interface ObjectDirective<T = any, V = any> {
  created?: DirectiveHook<T, null, V>;
  beforeMount?: DirectiveHook<T, null, V>;
  mounted?: DirectiveHook<T, null, V>;
  beforeUpdate?: DirectiveHook<T, VNode<any, T>, V>;
  updated?: DirectiveHook<T, VNode<any, T>, V>;
  beforeUnmount?: DirectiveHook<T, null, V>;
  unmounted?: DirectiveHook<T, null, V>;
  getSSRProps?: SSRDirectiveHook;
}
  • Директива о функциях

Директива Function — это упрощенная форма директивы Object, которая проще в использовании и подходит для сценариев, в которых необходимо выполнить всего несколько операций.

Обычно такое же поведение нужно только для mounted и updated, но другие хуки не нужны. В этом случае директива может быть определена непосредственно с помощью функции:

app.directive("color", (el, binding) => {
  // This will be called on both `mounted` and `updated`
  el.style.color = binding.value;
});

В исходном коде внутри определения типа интерфейса:

export type FunctionDirective<T = any, V = any> = DirectiveHook<T, any, V>;
export type DirectiveHook<T = any, Prev = VNode<any, T> | null, V = any> = (
  el: T,
  binding: DirectiveBinding<V>,
  vnode: VNode<any, T>,
  prevVNode: Prev
) => void;

⚠️ Советы и рекомендации

При использовании пользовательских D-директив необходимо помнить о ряде проблем:

1. Когда директива принимает несколько аргументов, передайте литерал объекта JS

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

app.directive("demo", (el, binding) => {
  console.log(binding.value.color); // => "white"
  console.log(binding.value.text); // => "hello!"
});

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

Поскольку компонент может содержать несколько корневых узлов, в отличие от attribute, директива не может быть передана другому элементу через v-bind="$attrs".

<MyComponent v-demo="test" />
<!-- template of MyComponent -->
<div>
  <!-- v-demo directive will be applied here -->
  <span>My component content</span>
</div>

3. Второй параметр директивы Custom поддерживает конфигурацию объекта

При определении директивы первый аргумент принимает объект, который содержит функцию ловушки директивы в дополнение к имени директивы, которое отличается от Vue2 и требует внимания.

app.directive("focus", {
  mounted(el) {
    el.focus();
  },
});

4. Перехватчики команд вызываются несколько раз для элементов, отображаемых v-for

<ul>
  <li v-for="item in list" v-focus>
</ul>

Функция ловушки директивы focus вызывается несколько раз с каждым элементом li в качестве аргумента.

5. Модификатор v-on .native больше не поддерживается

Редактор выдает предупреждение «'.native' modifier on 'v-on' directive is deprecated.»

<!-- will generate a warning, the .native modifier is deprecated -->
<input @click.native="doSomething" />

Используйте @click непосредственно в Vue3 для прослушивания нативных событий.

💡 Примеры использования

Ниже приведены 3 примера использования:

v-превью

Реализуйте предварительный просмотр изображения с помощью директивы v-preview Custom.

Реализация директивы:

export default {
  mounted(el) {
    el.addEventListener("mouseenter", (e) => {
      const img = e.target;
      const src = img.src;
      const parent = img.closest(".img-preview-container");
      parent.style.position = "relative";
      const preview = document.createElement("div");
      preview.style.position = "absolute";
      preview.style.top = 0;
      preview.style.left = 0;
      preview.style.background = "url(" + src + ") no-repeat center center";
      preview.style.backgroundSize = "contain";
      preview.style.width = "100%";
      preview.style.height = "100%";
      parent.append(preview);
    });
    el.addEventListener("mouseleave", (e) => {
      const parent = e.target.closest(".img-preview-container");
      parent.style.position = "";
      const preview = parent.querySelector("div");
      preview.remove();
    });
  },
};

Регистрация Пользовательская директива:

import { createApp } from "vue";
import vPreview from "./directives/vPreview";
import App from "./App.vue";
const app = createApp(App);
app.directive("preview", vPreview);
app.mount("#app");

Используйте пользовательскую директиву:

<div class="img-preview-container">
  <img v-for="src in imgSrcs" :src="src" v-preview />
</div>

При наведении курсора на элемент img отображается предварительный просмотр соответствующего изображения в соответствии с его src. При перемещении мыши предварительный просмотр изображения исчезает. Эта директива v-preview Custom позволяет нам быстро реализовать интерактивный эффект предварительного просмотра изображения.

Директива показывает и скрывает предварительный просмотр изображения, прослушивая события mouseenter и mouseleave, и использует метод closest для получения родительского контейнера элемента img и добавления к нему изображения предварительного просмотра.

2. v-верхний регистр

Директива v-uppercase Custom включает автоматическое преобразование текста в верхний регистр.

Реализация директивы:

export default {
  created(el, binding) {
    el.innerHTML = binding.value.toUpperCase();
  },
  update(el, binding) {
    el.innerHTML = binding.value.toUpperCase();
  },
};

Регистрация Пользовательская директива:

import { createApp } from "vue";
import vUppercase from "./directives/vUppercase";
import App from "./App.vue";
const app = createApp(App);
app.directive("uppercase", vUppercase);
app.mount("#app");

Используйте пользовательскую директиву:

<p v-uppercase>hello</p>

На странице отображается текст «HELLO». Директива v-uppercase Custom вызывает метод toUpperCase() в хуках created и update для преобразования текста в верхний регистр и обновления innerHTML.

3. v-изменить размер

Директива v-resize Custom позволяет прослушивать изменения ширины окна и выполнять методы обратного вызова.

Реализация директивы:

export default {
  mounted(el, binding) {
    const callback = binding.value;
    window.addEventListener("resize", () => {
      callback(el.offsetWidth);
    });
  },
};

Регистрация Пользовательская директива:

import { createApp } from "vue";
import vResize from "./directives/vResize";
import App from "./App.vue";
const app = createApp(App);
app.directive("resize", vResize);
app.mount("#app");

Используйте пользовательскую директиву:

<script setup lang="ts">
const onResize = (width) => {
  console.log(width);
};
</script>
<template>
  <div v-resize="onResize">Width</div>
</template>

Директива v-resize Custom вызывает связанную функцию обратного вызова при изменении размера окна и передает значение offsetWidth элемента. В методе onResize мы можем соответственно обработать новую ширину width элемента, например:

  • корректировка стиля
  • вызвать API для повторной выборки данных
  • изменить порядок страниц и т. д.

Эти директивы относительно просты, но очень широко используются в реальных проектах.

  • v-scroll директива события прокрутки;
  • v-mouseenter / v-mouseleave директивы событий входа/выхода мыши;
  • v-longpress директива длинного нажатия;

Это может быть очень полезно для упрощения нашего кода и повышения эффективности разработки.

🖌️ Как использовать в рендере функцию

1. Введение

Чтобы использовать директиву Custom в функции рендеринга Vue3, вам нужно использовать функцию withDirectives(), которая имеет следующую сигнатуру функции:

function withDirectives(
  vnode: VNode, // Elements that need to be bound with custom instructions
  directives: DirectiveArguments
): VNode;

// Custom array of directives, array form: [directive, value, argument, modifiers]
// The tail element of the array can be omitted if not needed.
type DirectiveArguments = Array<
  | [Directive]
  | [Directive, any]
  | [Directive, any, string]
  | [Directive, any, string, DirectiveModifiers]
>;

Используйте пользовательскую директиву:

import { h, withDirectives } from "vue";
// A Custom directive
const pin = {
  mounted() {
    /* ... */
  },
  updated() {
    /* ... */
  },
};
// <div v-pin:top.animate="200"></div>
const vnode = withDirectives(h("div"), [[pin, 200, "top", { animate: true }]]);

2. Примеры использования

Возьмите в качестве примера директиву v-focus Custom, вы можете реализовать ее следующим образом:

Импорт функций директивы withDirectives и Custom:

import { withDirectives } from "vue";
import { focus } from "./directives";

Используйте функцию withDirectives() в функции рендеринга и передайте аргументы по порядку:

const vnode = h("input", {
  type: "text",
  modelValue: "example",
  onInput: (event) => {
    // ...
  },
});
const app = {
  render() {
    return withDirectives(vnode, [[focus, true]]);
  },
};

vnode в этом примере кода — это виртуальный узел для элемента input, focus — это функция директивы v-focus Custom, а true — это массив параметров, переданных в директиву Custom для указания автоматического фокуса после того, как элемент вставлен в документ.

📚 Резюме

В этой статье представлены основы использования директивы Custom в Vue.js 3.x, включая определение и регистрацию пользовательских функций директив, параметров в функциях директив и функций-ловушек. Пользовательские директивы — очень важное расширение среды Vue.js, позволяющее разработчикам настраивать директивы для упрощения и повышения эффективности разработки в соответствии со своими потребностями.

Я надеюсь, что эта статья поможет вам изучить директиву Vue.js Custom.

📖 Учебные материалы

Вот некоторые учебные материалы, которые я лично считаю подходящими для директивы Vue3 Custom:

  1. Vue.js: Пользовательская директива

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

2. Vue Mastery: пользовательская директива Vue 3

Vue Mastery — отличная образовательная онлайн-платформа для Vue.js, а их курс Vue 3 Custom directive — отличный ресурс для изучения того, как использовать и практиковать Custom directive в Vue.js 3.x.

3. Директивы Vue 3: подробное подробное руководство

Эта статья начинается с основ директив, знакомит с использованием встроенных и пользовательских директив в Vue.js, а также иллюстрирует роль и использование директив с помощью практических сценариев применения и примеров.

Если вам нравится изучать JavaScript, вы можете подписаться на меня в Medium или Twitter, чтобы узнать больше о JavaScript!

Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .