Custom Elements с Vue

Custom Elements (пользовательские элементы) представляют собой стандартизированный способ создания повторно используемых HTML-компонентов, которые могут работать вне зависимости от фреймворка. Vue.js начиная с версии 3 поддерживает интеграцию с Custom Elements через механизм Vue Custom Element Wrapper, позволяя экспортировать компоненты Vue как нативные веб-компоненты.

Ключевой момент: при использовании Custom Elements Vue-компонент становится самодостаточным элементом, который можно вставлять в любой HTML-документ, включая проекты на других фреймворках.


Создание простого Custom Element

Для создания пользовательского элемента необходимо подготовить обычный Vue-компонент и обернуть его с помощью функции defineCustomElement:

import { defineCustomElement } from 'vue';
import MyComponent from './MyComponent.vue';

const MyCustomElement = defineCustomElement(MyComponent);

customElements.define('my-custom-element', MyCustomElement);

Объяснение кода:

  • defineCustomElement принимает обычный компонент Vue и возвращает конструктор, совместимый с браузерным API customElements.
  • customElements.define регистрирует элемент с уникальным именем, которое должно содержать дефис (-) по стандарту HTML.

После этого <my-custom-element></my-custom-element> можно использовать в любом HTML-документе.


Работа с пропсами и атрибутами

Custom Elements поддерживают передачу данных через атрибуты HTML. Vue автоматически синхронизирует атрибуты с пропсами компонента:

<template>
  <p>{{ message }}</p>
</template>

<script>
export default {
  props: {
    message: {
      type: String,
      default: 'Привет, мир!'
    }
  }
}
</script>

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

<my-custom-element message="Hello from HTML"></my-custom-element>

Vue преобразует атрибут message в пропс message внутри компонента. Важно: для передачи сложных объектов или массивов потребуется сериализация в JSON и десериализация внутри компонента.


События Custom Elements

Vue-компоненты в виде Custom Elements могут генерировать стандартные события DOM, что позволяет взаимодействовать с ними вне Vue-контекста:

// В компоненте
export default {
  methods: {
    emitEvent() {
      this.$emit('custom-event', { data: 123 });
    }
  }
}

HTML-обработчик:

<my-custom-element id="example"></my-custom-element>

<script>
  const el = document.getElementById('example');
  el.addEventListener('custom-event', event => {
    console.log(event.detail); // { data: 123 }
  });
</script>

Особенности:

  • this.$emit генерирует событие с использованием CustomEvent, доступное в обычном DOM.
  • События синхронизируются с нативной моделью событий браузера.

Жизненный цикл Custom Elements

Custom Elements поддерживают стандартные коллбэки жизненного цикла:

  1. connectedCallback – вызывается при вставке элемента в DOM.
  2. disconnectedCallback – вызывается при удалении элемента из DOM.
  3. attributeChangedCallback – реагирует на изменения атрибутов.

Vue автоматически связывает эти коллбэки с жизненным циклом компонента, что обеспечивает корректную инициализацию и уничтожение:

  • mounted компонента Vue соответствует connectedCallback.
  • unmounted – соответствует disconnectedCallback.

Scoped стили и Shadow DOM

Custom Elements могут использовать Shadow DOM, что гарантирует изоляцию стилей. В Vue это достигается настройкой shadow: true при создании кастомного элемента:

const MyCustomElement = defineCustomElement(MyComponent, { shadow: true });
customElements.define('my-shadow-element', MyCustomElement);

Преимущества Shadow DOM:

  • Стили компонента не влияют на остальную страницу.
  • Внешние CSS-файлы не нарушают визуальное оформление элемента.
  • Можно использовать CSS-переменные для тонкой настройки стилей из внешнего контекста.

Динамическая загрузка и ленивый Custom Element

Vue позволяет загружать Custom Elements лениво, что полезно для оптимизации крупных проектов:

import { defineAsyncCustomElement } from 'vue';

const LazyElement = defineAsyncCustomElement(() =>
  import('./HeavyComponent.vue')
);

customElements.define('lazy-element', LazyElement);
  • Элемент загружается только при первом использовании в DOM.
  • Подходит для тяжелых компонентов с большим количеством зависимостей.

Ограничения и особенности

  • Имена Custom Elements должны содержать дефис, иначе браузер выдаст ошибку.
  • Передача сложных объектов через атрибуты требует сериализации.
  • Состояние компонента хранится внутри элемента; синхронизация с внешним состоянием должна осуществляться через события или глобальные хранилища.
  • Shadow DOM ограничивает наследование глобальных стилей.

Интеграция с другими фреймворками

Custom Elements, созданные на Vue, полностью совместимы с React, Angular, Svelte или чистым Jav * aScript:

<my-custom-element message="Привет"></my-custom-element>

<script>
  document.querySelector('my-custom-element')
          .addEventListener('custom-event', e => console.log(e.detail));
</script>

Это делает Vue Custom Elements удобным инструментом для микрофронтенд-архитектуры, где разные части приложения могут использовать разные технологии.


Практические рекомендации

  • Использовать Custom Elements для модульных и изолированных компонентов, которые могут понадобиться в разных проектах.
  • Выносить глобальные зависимости, чтобы компонент оставался самодостаточным.
  • Использовать Shadow DOM для компонентов с уникальными стилями, требующими полной изоляции.
  • События и атрибуты следует проектировать явно и предсказуемо, чтобы обеспечить корректную интеграцию с другими фреймворками или нативным DOM.