Мир веб-компонентов: разбираемся в трендах

Kate

Administrator
Команда форума
Недавно я работал над исследованием технологии веб-компонентов для своей компании и обнаружил, что относительно сложно понять глобальное положение вещей на данный момент. Этой статьей я хочу попробовать решить эту проблему и предоставить вам отправную точку в путешествии в мир веб-компонентов.

Темы для рассмотрения​

  • Поддержка браузерами.
  • Совместимость с фреймворками.
  • SSR + SEO/Боты.
  • Стилизация компонентов.
  • Специальные возможности (Accessibility).
  • Версионирование.
  • Загрузка в браузер.
  • Доступные инструменты.
  • Веб-компоненты vs Фреймворки.

Поддержка браузерами​

IE 10–11 и Edge (с движком Chakra) находятся вне игры, поскольку они не поддерживают Shadow DOM. Это делает использование веб-компонентов нецелесообразным из-за сложности полифилов.

Все остальные браузеры (Chrome, FF, Safari, учитывая две последние версии) прекрасно работают со всеми основными технологиями, которые нам нужны. Давайте взглянем на таблицу.

ТехнологияChromeFFSafariiOSAndroid
(CSS) ::slottedxxx (баги)xx
(CSS) :hostxxxxx
(CSS) :host()
Есть обходной путь
xx--x
(CSS) :host-context()x---x
(CSS) :rootxxxxx
(CSS) CSS Variablesxxxxx
(JS) Constructible Stylesheets
Есть обходной путь
x----
(JS) Custom Elementsxxxxx
(JS) Shadow DOMxxxxx
(JS) Custom Eventxxxxx

Совместимость с фреймворками​

Все основные фреймворки, которые существуют в настоящее время, полностью поддерживают веб-компоненты. Но давайте посмотрим внимательно:

SSR (Рендеринг на стороне сервера) + SEO/Боты​

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

Так что же нам тогда делать?

Ответ прост — вы можете жить без SSR для веб-компонентов. Конечно, этот подход имеет некоторые ограничения, но приходится работать с тем, что есть... Давайте посмотрим детальнее.

Чтобы рендерить веб-компоненты только на стороне клиента и не жертвовать SEO/совместимостью с ботами, вам нужно хранить контент в Light DOM и использовать атрибуты ARIA. Рассматривайте свои веб-компоненты как нативные элементы HTML. Вы можете согласиться с тем, что боту не нужно видеть Shadow DOM, например, для элемента <select>, чтобы просмотреть его содержимое. Если компоненты хорошо спроектированы, ботам не нужно развернутое (Shadow + Light) DOM-дерево для получения текстового содержимого.

Давайте рассмотрим разметку компонента «Tab» в качестве примера:

<my-tab-group role="tablist" aria-label="My test tabs">
<my-tab role="tab" slot="tab">Title for tab 1</my-tab>
<my-tab-panel role="tabpanel" slot="panel">Content 1</my-tab-panel>
<my-tab role="tab" slot="tab">Title for tab 2</my-tab>
<my-tab-panel role="tabpanel" slot="panel">Content 2</my-tab-panel>
<my-tab role="tab" slot="tab">Title for tab 3</my-tab>
<my-tab-panel role="tabpanel" slot="panel">Content 3</my-tab-panel>
</my-tab-group>
Как вы можете видеть из этого примера, боту не нужен доступ к Shadow DOM для понимания содержимого, представленного на странице.

Стилизация компонентов​

Поскольку в наших компонентах мы используем Shadow DOM для внутренней разметки и слоты для контента, написание CSS поначалу может быть нетривиальной задачей. Давайте рассмотрим основные концепции, которые нужно держать в уме.



Изоляция CSS​

Shadow DOM практически полностью изолирует свое содержимое от CSS, включенных на странице. В примере, приведенном выше, тег <p> (который находится внутри Shadow DOM) имеет текст, окрашенный в зеленый цвет, поскольку CSS свойство «color» было унаследовано.

Как включить CSS в Shadow DOM​

В настоящее время самый простой способ включения CSS для веб-компонентов — это встраивание их inline в шаблон веб-компонента. Если вы включите их через тег <link>, вы увидите FOUC во время загрузки страницы, точно так же, как в примере выше (обратите внимание на текст «I’m Shared CSS»).

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

Поэтому остается ждать поддержки спецификации Constructable Stylesheets во всех основных браузерах. Это дало бы нам больше контроля над CSS.

Контекстно-зависимые стили​

Часто наши компоненты имеют разные стили отображения в зависимости от переданных им аргументов. Классический пример — <select multiple>. Когда вы передаете аргумент «multiple» компоненту «select» — он полностью меняет свой внешний вид. Для достижения аналогичного поведения веб-платформа предлагает псевдокласс :host() (не путайте с :host).

К сожалению, этот псевдокласс не поддерживается в Safari и iOS, поэтому мы не можем его использовать. В качестве обходного пути мы можем использовать возможности JS для ручного проецирования атрибутов и классов хост-элемента в корневой элемент Shadow DOM, чтобы получить возможность писать обычный CSS.

Стилизация контента внутри слота (<slot>)​

Таблицы стилей, которые были добавлены в Shadow DOM, могут также применяться в элементах, находящихся внутри <slot>. Для этого у нас есть псевдоэлемент ::slotted(). Однако его поддержка браузерами все еще не идеальна. Посмотрите на приведенный выше пример: Safari не будет правильно обрабатывать селектор ::slotted(p)::before.

В качестве обходного пути мы можем добавить стили, отвечающие за стилизацию содержимого слота прямо на страницу (в Light DOM). Таким образом, следующий селектор ::slotted(p)::before для <my-component> будет преобразован в my-component p::before. Это, естественно, несколько нарушает концепцию Shadow DOM, однако я не вижу другого пути на текущий момент.

Рекомендации​

На этом этапе я бы рекомендовал продолжать (если вы уже это делаете) загружать CSS на страницу отдельным файлом с помощью тега <link> и включать этот тег в каждое Shadow DOM-дерево. В настоящее время я использую следующий код для этого:

class MyComponent extends HTMLElement {

constructor() {
super();

this.attachShadow({ mode: 'open' });
this.__injectGlobalCSS();
};

__injectGlobalCSS() {
const globalCssInclude = document.querySelector(
'head > [data-global-css="true"]'
);

if (globalCssInclude === null) {
console.warn(`Can't find global CSS for component ${this.tagName}! Trying to render w/o it...`);
return;
}

this.shadowRoot.appendChild(globalCssInclude.cloneNode(true));
}

};

Специальные возможности (Accessibility)​

Когда пользователи специальных возможностей, таких как скринридеры, перемещаются по странице, жизненно важно, чтобы передавалось семантическое значение различных элементов управления. Но как этого добиться с помощью веб-компонентов, учитывая, что теги HTML будут нестандартными и, как следствие, не будут иметь никакого семантического значения?

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

Давайте посмотрим на доступный компонент-слайдер, созданный с помощью технологии веб-компонентов:

<custom-slider min="0" max="10" value="3" role="slider"
tabindex="0" aria-valuemin="0" aria-valuemax="10"
aria-valuenow="3" aria-valuetext="3"
aria-label="Movie rating"></custom-slider>
Чтобы увидеть больше примеров полностью доступных веб-компонентов, вы также можете обратиться к следующим инструкциям от Google.

Версионирование​

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

Давайте посмотрим на варианты, которые у нас есть.

Никогда не делайте breaking changes. Это принцип, который используют браузеры. И хотя это возможно сделать, и это может быть даже наилучшим вариантом для начала, очевидно, что данный подход противоречит принципу «fail fast, fail safe» и не способствует инновациям.

Управление версиями на основе тегов. Таким образом, вместо <x-button> у вас будет <x-button-v1> для того, чтобы иметь несколько мажорных версий компонента на странице. Поэтому, если для «Фрагмента 1» требуется button@1.1.5, а для «Фрагмента 2» требуется button@1.2.1 — будет использоваться только button@1.2.1. И если для «Фрагмента 1» требуется версия 1.1.5, а для «Фрагмента 2» требуется версия 2.0.0 — оба компонента будут зарегистрированы.

Версионирование по скоупу фрагмента. Таким образом, вместо <x-button> у вас будет <x-button-myfragment>.

Загрузка в браузер для микросервисного фронтенда​

Вы можете пропустить эту секцию, если у вас есть одно приложение на фронтенде. Более подробную информацию о микросервисах на фронтенде можно найти здесь.

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

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

Доступные инструменты​

Библиотеки, которые вы можете использовать для создания своих веб-компонентов (отсортировано по моим предпочтениям сверху вниз):

  • LitElement — написан ребятами из Polymer, Google. На мой взгляд, самый оптимальный выбор.
  • Stencil — компилятор веб-компонентов плюс базовые классы. Создан командой Ionic. Его система сборки не очень совместима в Webpack.
  • SkateJS — крошечная обертка вокруг нативных API, которая позволяет использовать различные библиотеки рендера. Создано Trey Shugart, который подарил нам WC SSR PoC.
  • Svelte 3  — это скорее фреймворк, а не библиотека для упрощения создания веб-компонентов.
  • Riot.js.
  • Slim.js.
  • X-Tag — последняя версия все еще в бете.
  • Smart HTML Elements — платный.
Но, постойте, действительно ли мне нужен какой-то инструмент для комфортного написания веб-компонентов? И простой ответ — нет, вы можете написать их, используя Vanilla JS. И это будет работать для большинства простых компонентов, которые вы будете писать.

Веб-компоненты vs Фреймворки​

Если вкратце: они разные. Не пытайтесь заменить веб-компонентами старые добрые фреймворки, такие как Vue.js или React.

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

Другой вариант использования — если вы пишете open source библиотеку UI-компонентов (например, Bootstrap или MDC) и хотите сохранить ее независимой от фреймворка.

Пример Material Design Components в эру до веб-компонентов и с веб-компонентами.

Со временем фреймворки, скорее всего, начнут использовать Custom Elements и Shadow DOM внутри, но я не ожидаю увидеть широкого распространения данного подхода в ближайшие 1-2 года.

Источник статьи; https://dou.ua/lenta/articles/web-components-2019/
 
Сверху