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

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

Теми для розгляду

Підтримка браузерами

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

Всі інші браузери Chrome, FF, Сафарі, враховуючи дві останні версії) чудово працюють з усіма основними технологіями, які нам потрібні. Давайте глянемо на таблицю.

Технологія Chrome FF Safari iOS Android
(CSS) ::slotted x x x (баги ) x x
(CSS) :host x x x x x
(CSS) :host()
Є обхідний шлях
x x - - x
(CSS) :host-context() x - - - x
(CSS) :root x x x x x
(CSS) CSS Variables x x x x x
(JS) Конструктивні Stylesheets
Є обхідний шлях
x - - - -
(JS) Custom Elements x x x x x
(JS) Shadow DOM x x x x x
(JS) Cistom Event x x x x x

Сумісність з фреймворками

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

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 спочатку може бути нетривіальним завданням. Давайте розглянемо основні концепції, які потрібно тримати в розумі.


See the Pen codepen.io/stylet/pen/GbzKRg ">wc-host-styling by Vlad Fedosov
(codepen.io/stylet ">@stylet) on codepen.io «>CodePen.

Ізоляція 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 component for ${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>.

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

Ви можете пропустити цей розділ, якщо у вас є один додаток на фронтенде. Більш детальну інформацію про микросервисах на фронтенде можна знайти тут .

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

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

Доступні інструменти

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

Але, стривайте, дійсно мені потрібен якийсь інструмент для комфортного написання веб-компонентів? І просту відповідь — ні, ви можете написати їх, використовуючи Vanilla JS. І це буде працювати для більшості простих компонентів, які ви будете писати.

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

Якщо коротко: вони різні. Не намагайтеся замінити веб-компонентами старі добрі фреймворки, такі як Vue.js або React.

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

Інший варіант використання — якщо ви пишете open source бібліотеку UI-компонентів (наприклад, Bootstrap або MDC) і хочете зберегти її незалежною від фреймворка.

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

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

Примітки

Якщо ви бачите будь-які прогалини або помилки в статті, напишіть у коментарях або надішліть мені на пошту .

У разі позитивного відгуку від спільноти, я сподіваюся продовжити цю тему і напишу про нюанси використання веб-компонентів.

Корисні посилання

Опубліковано: 11/07/19 @ 10:00
Розділ Різне

Рекомендуємо:

Поради сеньйорів: як прокачати знання junior HR/Recruiter
EcoCity — мережа громадського моніторингу якості повітря за допомогою пристроїв на Arduino
Безпечний шлях. Що таке SAFe і як його впровадити
Python дайджест #21: Python 3 у Windows 10 Store. Black стає частиною Python Software Foundation
.NET дайджест #28: introducing .NET 5, asynchronous Injection, Core dump of StackOverflowException