Веб-компоненты – это пользовательские элементы, Shadow DOM и HTML-шаблоны, то есть примитивы платформы для многократно используемых компонентов, не зависящих от фреймворка, разметка и стили которых инкапсулированы из документа. Используйте их для систем проектирования, встраиваемых виджетов и автономных интерфейсов.
Правила ниже являются сквозными. Найдите руководство по нужной задаче. В каждом из них описан контекст, а также API и возможные проблемы.
Решите, нужен ли вам вообще пользовательский элемент
Используйте пользовательские элементы, только когда вам нужно инкапсулированное поведение, жизненный цикл или повторно используемый API на основе JS в теге. Не создавайте его, если:
- Достаточно простого элемента HTML с CSS. Стилизованный элемент
<button>или<details>доступнее и дешевле, чем повторная реализация. - Объект предназначен только для представления и не имеет поведения. Класс нативного элемента более эффективен и может быть ограничен с помощью
@layerили@scope. - Вам нужна только область видимости стилей на сервере без интерактивности. Достаточно таблицы стилей с ограниченной областью видимости или декларативной теневой модели DOM (без обновления).
Переопределение встроенной семантики (создание собственного элемента <my-button>, который заново определяет фокус, активацию и ARIA) – самая распространенная ошибка. Вы получаете доступ к специальным возможностям платформы, только если используете встроенный элемент. Если вам действительно нужен контейнер в стиле <my-button> (например, для общих хуков стилей, дополнительной разметки или поведения, которое не может выразить нативный элемент), оберните настоящий элемент <button>, а не реализуйте его заново. О том, как перенаправлять стили во внутренний нативный элемент, читайте в статье Styling Web Components (Стилизация веб-компонентов).
Вам нужно решить, требуется ли вам инкапсулированное состояние, API с поддержкой скриптов или поведение, которое не может быть выражено нативным элементом, а не просто другой внешний вид. Переключатель "Нравится/Избранное" подходит: он имеет зафиксированное состояние домена, предоставляет это состояние в качестве хука стилизации для окружающей страницы и отправляет собственное событие change – ничего из этого стилизованный <button> вам не дает. Кнопка, которая только меняет цвет при нажатии, не является интерактивной. Используйте <button> и CSS. При создании элемента смоделируйте его состояние с помощью специального состояния или атрибута (см. Пользовательские элементы).
Сопоставьте инструмент с базой кода
Нужно ли использовать пользовательский элемент, зависит от того, что уже используется в проекте. Агенты склонны к чрезмерному абстрагированию и слишком охотно используют новые элементы. Если вы сомневаетесь, обычно лучше выбрать более простой вариант (нативный элемент, CSS, шаблон сервера), а затем, если потребуется, перейти к пользовательскому элементу. Эвристика в порядке приоритета:
- В базе кода уже есть модель компонента (React, Vue, Svelte и т. д.). По умолчанию используйте компонент этого фреймворка для интерфейса приложения. Используйте пользовательский элемент, только если он должен существовать вне фреймворка или дольше него. Например, это может быть примитив системы проектирования, общий для приложений или фреймворков, виджет, помещенный в разметку, которую фреймворк не контролирует, или выходные данные, которые должны работать как обычный HTML (электронная почта, CMS, фрагменты SSR). Не создавайте параллельную систему компонентов для обычных представлений в приложении.
- Никаких инструментов, в основном статическая разметка. Предпочтительно использовать обычный HTML + CSS или шаблон/фрагмент сервера. Специальный элемент оправдывает свое существование только тогда, когда вам действительно нужно поведение, жизненный цикл или API на основе JS в теге, а не просто для совместного использования разметки, что делает включение сервера или
<template>без отправки JS. - Генератор статических сайтов с интерактивными элементами. Разметка должна быть статической, а пользовательский элемент – интерактивным. Если у вас много разметки и мало интерактивности, оставьте разметку в Light DOM (или Declarative Shadow DOM) и позвольте небольшому пользовательскому элементу постепенно улучшать ее (прогрессивное улучшение), а не перерисовывать все из JS.
- Вместо этого вы можете расширить встроенный элемент. Использование встроенного элемента (CSS, несколько прослушивателей, случайный псевдоэлемент) обычно предпочтительнее, чем пользовательский элемент, поскольку вы сохраняете доступность и поведение платформы бесплатно. Переходите на пользовательский элемент, когда обходные пути перестают поддерживаться — когда вы боретесь со встроенным поведением элемента или когда опыт разработки становится хуже, чем был бы чистый API компонента. Необходимость добавить немного JS-кода во встроенный элемент – не повод заменять его.
Общие правила
Они применимы ко всем руководствам ниже.
- ОБЯЗАТЕЛЬНО. Название тега должно быть написано в формате kebab-case и содержать хотя бы один дефис (например,
my-element). Это позволяет отличать пользовательские элементы от текущих и будущих встроенных. - ОБЯЗАТЕЛЬНО. Сначала вызовите
super()в конструкторе, прежде чем обращаться кthis. Если вызвать его после любого оператора, который считывает или записываетthis, возникнет ошибка. - ОБЯЗАТЕЛЬНО: используйте семантические элементы (
<button>,<nav>,<ul>) в шаблонах теневых элементов. Большинство проблем со специальными возможностями в теневых деревьях возникает из-за несемантической разметки. Слот не меняет роль того, что он проецирует. - ОБЯЗАТЕЛЬНО. Ссылки ARIA
id(aria-labelledby,aria-describedby,aria-controls) не могут пересекать границу теневого дерева. Ссылающийся элемент иid, на который он указывает, должны находиться в одном дереве. - Рекомендуется использовать
attachShadow({ mode: 'open' }), чтобы инструменты разработчика, скрипты страниц и гидратация сервера могли получить доступ кelement.shadowRoot. Используйте'closed', только если вам нужно скрыть внутреннюю структуру от скриптов страницы. - Не используйте настраиваемые встроенные элементы (
extends HTMLButtonElementсis="…"), так как Safari не поддерживает их.
Матрица руководств по вариантам использования
Определите задачу и найдите руководство по ее выполнению:
| Если вам нужно… | Получить |
|---|---|
| Определите новый семантический тег с поведением и жизненным циклом | Пользовательские элементы |
| Ограничивать область действия стилей и разметки компонентом и проецировать в него контент пользователя | Shadow DOM, шаблоны и слоты |
| Отрисовка теневого дерева компонента на сервере (SSR) без кратковременного показа нестилизованного контента | Предварительная отрисовка пользовательских элементов |
Стилизация за пределами теневой границы (темы, ::part, настраиваемые свойства) |
Стилизация веб-компонентов |
Отправка значения пользовательского элемента управления в окружающий элемент <form> |
Специальные элементы, связанные с формой |
| Как сделать специальный элемент доступным (роли, фокус, ARIA в теневых корнях) | Доступные веб-компоненты |