W3docs

Web Components

Изучите Web Components с нуля: пользовательские элементы, Shadow DOM, HTML-шаблоны, слоты и колбэки жизненного цикла — с примерами и практиками.

Web Components позволяют создавать собственные переиспользуемые, инкапсулированные HTML-элементы — пользовательские теги, такие как <user-card> или <star-rating>, которые несут в себе собственную разметку, стили и поведение. Поскольку они построены на нативных браузерных API, они работают в любом фреймворке (или вообще без него) и изолируют свою внутреннюю реализацию от остальной части страницы. В этом руководстве рассмотрены три основных строительных блока Web Components, показан процесс создания компонента с нуля, а также описаны хуки жизненного цикла и лучшие практики.

Когда стоит использовать Web Components?

Используйте Web Components, когда нужен самодостаточный UI-виджет, который:

  • Работает везде — один и тот же <my-widget> можно встроить в страницу на React, Vue, чистом HTML или серверной разметке без дополнительного шага сборки.
  • Остаётся изолированным — его стили не могут вытечь наружу, а стили страницы не могут проникнуть внутрь благодаря Shadow DOM.
  • Поставляется как единый файл — разметка, стили и логика живут вместе, поэтому компонент легко распространять и переиспользовать.

Дизайн-системы, встраиваемые виджеты и общие библиотеки компонентов — наиболее распространённые варианты применения.

Введение в Web Components

Web Components — это набор API веб-платформы, позволяющий создавать новые пользовательские, переиспользуемые, инкапсулированные HTML-теги. В совокупности работают три технологии:

  • Custom Elements — определяют новые HTML-теги и их поведение.
  • Shadow DOM — инкапсулирует разметку и стили компонента, ограничивая их область видимости элементом.
  • HTML Templates — позволяют объявлять переиспользуемые фрагменты инертной разметки, которые вы применяете во время выполнения.

Custom Elements

Custom Elements определяют новые HTML-теги и их поведение. Они основаны на классах JavaScript: пользовательский элемент — это класс, расширяющий HTMLElement. После регистрации тег можно использовать как любой стандартный HTML-элемент.

<div>
  <script>
    class MyElement extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = '<p>Hello, World!</p>';
      }
    }
    customElements.define('my-element', MyElement);
  </script>
  <my-element></my-element>
</div>

Этот фрагмент кода демонстрирует, как создать пользовательский HTML-элемент с именем my-element. Класс MyElement расширяет HTMLElement, прикрепляет Shadow DOM для инкапсуляции содержимого и вставляет простой абзац с текстом "Hello, World!".

Shadow DOM

Shadow DOM обеспечивает инкапсуляцию для пользовательских элементов. Он позволяет прикрепить к элементу отдельное скрытое DOM-дерево (теневое дерево), сохраняя стили и разметку в области видимости самого элемента. Подробнее — в отдельной главе о JavaScript Shadow DOM.

<div>
  <script>
    class ShadowElement extends HTMLElement {
      constructor() {
        super();
        let shadow = this.attachShadow({ mode: 'open' });
        shadow.innerHTML = `
          <style>
            p { color: blue; }
          </style>
          <p>Shadow DOM content</p>
        `;
      }
    }
    customElements.define('shadow-element', ShadowElement);
  </script>
  <shadow-element></shadow-element>
</div>

В этом примере класс ShadowElement расширяет HTMLElement и прикрепляет теневой корень к элементу. Параметр mode: 'open' указывает, что Shadow DOM доступен через JavaScript. При значении mode равном 'open' доступ к теневому корню можно получить через свойство element.shadowRoot. Если mode установлен в 'closed', теневой корень недоступен извне, что усиливает инкапсуляцию и предотвращает прямое управление Shadow DOM внешними скриптами.

В Shadow DOM мы определяем стили и HTML-контент. Стили (в данном случае абзац, оформленный синим текстом) ограничены областью видимости Shadow DOM, что гарантирует их отсутствие влияния на другие элементы страницы. Такая инкапсуляция полезна при создании переиспользуемых компонентов с согласованными стилями и поведением независимо от контекста их использования.

Внимание

Используйте локальные стили внутри Shadow DOM, чтобы предотвратить утечку стилей и обеспечить их ограничение областью видимости компонента.

Подробнее об ограничении CSS областью видимости теневого дерева — включая :host и CSS-переменные, которые могут пересекать границу — см. раздел Стилизация Shadow DOM.

HTML Templates

HTML-шаблоны позволяют определять переиспользуемые фрагменты HTML, которые являются инертными — они разбираются, но не отображаются, и их скрипты не выполняются — до тех пор, пока вы не клонируете и не вставите их во время выполнения. Элемент <template> — стандартный способ сделать это.

<div>
  <template id="my-template">
    <style>
      p { color: red; }
    </style>
    <p>This is a template content</p>
  </template>
  <script>
    const template = document.getElementById('my-template').content;
    document.body.appendChild(template.cloneNode(true));
  </script>
</div>

Этот пример показывает, как использовать HTML-шаблоны. Тег <template> содержит HTML и стили, которые не отображаются немедленно. Скрипт клонирует содержимое шаблона и добавляет его в тело документа, делая его видимым.

Создание первого Web Component

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

Шаг 1: Определение компонента

Создайте новый класс, расширяющий HTMLElement.

<div>
  <script>
    class MyComponent extends HTMLElement {
      constructor() {
        super();
        const shadow = this.attachShadow({ mode: 'open' });
        shadow.innerHTML = 'Hello, World!';
      }
    }
    customElements.define('my-component', MyComponent);
  </script>
  <my-component></my-component>
</div>

Этот фрагмент закладывает основу для веб-компонента с именем my-component. В нём определён класс, расширяющий HTMLElement, и прикреплён Shadow DOM — готово к дальнейшей настройке.

Шаг 2: Добавление стилей и шаблонов

Используйте Shadow DOM для инкапсуляции стилей и шаблонов.

<div>
  <script>
    class MyStyledComponent extends HTMLElement {
      constructor() {
        super();
        let shadow = this.attachShadow({ mode: 'open' });
        shadow.innerHTML = `
          <style>
            p { font-size: 20px; color: green; }
          </style>
          <p>This is my styled component!</p>
        `;
      }
    }
    customElements.define('my-styled-component', MyStyledComponent);
  </script>
  <my-styled-component></my-styled-component>
</div>

Этот фрагмент улучшает my-styled-component, добавляя ограниченные стили внутри Shadow DOM. Абзац внутри компонента оформлен зелёным цветом и увеличенным размером шрифта.

Шаг 3: Добавление интерактивности

Добавьте JavaScript, чтобы сделать компонент интерактивным.

<div>
  <script>
    class InteractiveComponent extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = `
          <style>
            p { font-size: 20px; }
          </style>
          <p>This is interactive!</p>
          <button>Click me</button>
        `;
        this.shadowRoot.querySelector('button').addEventListener('click', () => {
          this.shadowRoot.querySelector('p').textContent = 'You clicked!';
        });
      }
    }
    customElements.define('interactive-component', InteractiveComponent);
  </script>
  <interactive-component></interactive-component>
</div>

Этот пример показывает, как сделать веб-компонент интерактивным. InteractiveComponent включает кнопку, которая при нажатии изменяет текстовое содержимое абзаца внутри компонента.

Продвинутые техники Web Components

Колбэки жизненного цикла

Пользовательские элементы имеют колбэки жизненного цикла, позволяющие выполнять код на определённых этапах жизни элемента.

<div>
  <script>
    class LifecycleComponent extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
      }
      connectedCallback() {
        this.shadowRoot.innerHTML = '<p>Element added to page.</p>';
      }
      disconnectedCallback() {
        console.log('Element removed from page.');
      }
    }
    customElements.define('lifecycle-component', LifecycleComponent);
  </script>
  <lifecycle-component></lifecycle-component>
</div>

Этот фрагмент демонстрирует использование колбэков жизненного цикла в веб-компонентах. LifecycleComponent обновляет своё содержимое при добавлении на страницу и выводит сообщение в консоль при удалении.

Обработка изменений атрибутов

Реагируйте на изменения атрибутов элемента, определив массив observedAttributes и реализовав метод attributeChangedCallback.

<div>
  <script>
    class AttributeComponent extends HTMLElement {
      static get observedAttributes() {
        return ['data-text'];
      }
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = '<p></p>';
      }
      attributeChangedCallback(name, oldValue, newValue) {
        if (name === 'data-text') {
          this.shadowRoot.querySelector('p').textContent = newValue;
        }
      }
    }
    customElements.define('attribute-component', AttributeComponent);
  </script>
  <attribute-component data-text="Initial text"></attribute-component>
  <script>
    const element = document.querySelector('attribute-component');
    setTimeout(() => {
      element.setAttribute('data-text', 'Updated text');
    }, 2000);
  </script>
</div>

Этот пример показывает, как обрабатывать изменения атрибутов в веб-компоненте. AttributeComponent обновляет своё внутреннее содержимое при изменении атрибута data-text. Обратите внимание: attributeChangedCallback срабатывает только для атрибутов, перечисленных в observedAttributes — без этого списка изменения атрибутов игнорируются.

Четыре колбэка жизненного цикла пользовательских элементов:

КолбэкКогда вызывается
constructor()При создании или обновлении элемента. Здесь устанавливается начальное состояние, но не следует обращаться к атрибутам или дочернему DOM.
connectedCallback()Каждый раз при вставке элемента в документ. Лучшее место для рендеринга и добавления обработчиков событий.
disconnectedCallback()Каждый раз при удалении элемента из документа. Здесь нужно очищать обработчики и таймеры.
attributeChangedCallback(name, oldVal, newVal)При изменении отслеживаемого атрибута. Требует статического геттера observedAttributes.

Распределение дочерних элементов через слоты

<slot> позволяет компоненту отображать разметку, которую пользователь размещает между его тегами, так что потребители могут передавать собственный контент. Это основа композиции с Web Components — подробнее см. Shadow DOM, слоты и композиция.

<div>
  <script>
    class CardBox extends HTMLElement {
      constructor() {
        super();
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.innerHTML = `
          <style>
            .box { border: 1px solid #ccc; padding: 8px; }
          </style>
          <div class="box"><slot>Default content</slot></div>
        `;
      }
    }
    customElements.define('card-box', CardBox);
  </script>
  <card-box>Passed-in content</card-box>
  <card-box></card-box>
</div>

Первый <card-box> отображает "Passed-in content" внутри стилизованного блока; второй возвращается к стандартному содержимому слота — "Default content".

Лучшие практики Web Components

Разумное использование Shadow DOM

Инкапсулируйте стили и скрипты внутри Shadow DOM, чтобы предотвратить конфликты с другими элементами страницы.

Соблюдение соглашений об именовании

Имя пользовательского элемента должно содержать дефис (например, my-card, а не mycard), чтобы избежать конфликтов с текущими и будущими стандартными HTML-элементами. Регистрация имени без дефиса вызывает ошибку.

Модульность компонентов

Создавайте небольшие, переиспользуемые компоненты, чтобы кодовая база оставалась поддерживаемой и масштабируемой.

Отказ от глобальных стилей

Используйте локальные стили внутри Shadow DOM, чтобы предотвратить утечку стилей и обеспечить их ограничение областью видимости компонента.

Документация и тестирование

Тщательно документируйте свои компоненты и пишите тесты, чтобы убедиться в их корректной работе в разных браузерах и сценариях использования.

Заключение

Web Components — это универсальная и мощная возможность современной веб-разработки, позволяющая создавать переиспользуемые, инкапсулированные пользовательские элементы. Понимание и применение Custom Elements, Shadow DOM и HTML Templates даёт возможность создавать модульные, поддерживаемые и масштабируемые веб-приложения. Это руководство предоставило вам знания и примеры, необходимые для начала работы с Web Components, чтобы вы могли эффективно применять их в своих проектах.

Для дальнейшего изучения — исследуйте JavaScript Shadow DOM, узнайте, как взаимодействуют Shadow DOM и события, и подробно изучите элемент <template>.

Практика

Практика
Какое из следующих утверждений о Web Components является верным?
Какое из следующих утверждений о Web Components является верным?
Was this page helpful?