Опубликовано 24.06.2019 (изменено: 16.08.2022 ) в Web development.
10 маленьких (и не очень) причин, почему вам не стоит использовать web component (по крайней мере пока) в своем проекте.
Ничего из этой статьи не должно рассматриваться как критика большой работы, проделанной над веб компонентами. Возможно, я допустил ошибки в этом посте, тогда я готов их исправить.
9 советов проектному менеджеру
How to influence people
Черты лидера
Прогрессивное улучшение
Возможно, это очень старомодный взгляд, но я думаю, что сайты должны работать без JavaScript, где это возможно. Веб компоненты не работают.
И это нормально для вещей, которые являются интерактивными, например кастомные элементы форм ( ), но это ненормально для вашего navbar’а. Или рассмотрите простой элемент , который инкапсулирует всю логику для кнопки поделиться. Если бы я делал его в Svelte, то он бы генерировал на сервере HTML вроде такого:
Со включенным JavaScript оно подвержено «прогрессивному улучшению». Вместо создания новой вкладки, оно откроет небольшое попап окно. Но и без JS, оно всё еще работает.
Введение в веб-компоненты — полное пошаговое руководство
С другой стороны, html для web компонента выглядел бы как-то так:
… который бесполезен и недоступен, если JS выключен или каким-то образом сломан, или если пользователь использует старый браузер.
Класс class=»svelte-1jnfxx» является тем, что позволяет инкапсулировать стили без использования Shadow DOM, что подводит меня к следующему пункту.
CSS в, брр, JavaScript
Если вы хотите использовать Shadow DOM для инкапсуляции стилей, вам придется включить ваш CSS в элемент. Единственный способ сделать это, по крайней мере, если вы хотите избежать FOUC, — включить CSS в качестве строки в JS модуле, который определяет элемент.
FOUC (Flash of unstyled content) — кратковременное появление html-содержимого с дефолтными стилями браузера до загрузки CSS страницы. — примечание переводчика
И это идет вразрез с советом по оптимизации, который звучит как «меньше JavaScript, пожалуйста». Технология CSS-in-JS подверглась критике в том числе из-за того, что подразумевает отсутствие css в .css файлах, от чего мы никуда не ушли.
В будущем, возможно, мы сможем использовать CSS модули вместе с Constuctable Stylesheets для решения этой проблемы. И мы сможем использовать ::theme и ::part для стилизации элементов в Shadow DOM. Но и они не свободны от проблем.
Болезнь платформ
На момент написания существует 61 000 открытых issues на https://crbug.com, баг трекере Chromium, что отражает огромную сложность создания современного браузера.
Каждый раз, когда мы добавляем функцию в платформу, мы увеличиваем эту сложность — создавая новую плоскость для багов, и делая всё менее вероятным появление конкурента Chromium.
Это также создает сложность для разработчиков, которых поощряют и принуждают изучать эти новые функции (некоторые из которых, например, HTML Imports или изначальная спецификация Custom Elements, никогда не использовались за пределами Google, а после были удалены).
Web Components
Полифилы (Polyfills)
Не улучшает ситуацию и то, что нам приходится использовать полифилы, если мы хотим поддерживать все браузеры. Это не помогает материалам по Constructable Stylesheets, написанным сотрудником Google, не упоминающим, что это функция только Chrome. (Все три редактора спецификации являются сотрудниками Google. Похоже что у Webkit есть некоторые сомнения по некоторым аспектам дизайна)
Композиция
Компоненту полезно иметь возможность контролировать когда его контент отрисовывается. Допустим, что мы хотим использовать , чтобы показать некоторую документацию из сети, только когда она станет видимой.
Toggle the section for more info:
Сюрприз! Даже несмотря на то, что вы еще не переключили секцию, браузер уже загрузил more-info.html вместе со всеми изображениями и другими ресурсами, на которые он ссылается.
Это вызвано тем, что скрытый контент отрисовывается предварительно в custom elements. И тут выясняется, что в большинстве случаев вы хотите, чтобы скрытый контент отрисовывался лениво ( lazy ). Svelte v2 использует eager отрисовку, чтобы соответствовать Web стандартом, и эта модель стала крупным источником фрустрации — мы не могли сделать эквивалент React Router, например. В Svelte v3 мы отказались от модели композиции custom element и не пожалели об этом.
Увы, это только фундаментальные особенности DOM, что приводит нас к…
Путаница между props и attributes
Props и attributes — почти одинаковые вещи, верно?
const button = document.createElement(‘button’); button.hasAttribute(‘disabled’); // false button.disabled = true; button.hasAttribute(‘disabled’); // true button.removeAttribute(‘disabled’); button.disabled; // false
Я имею ввиду почти:
typeof button.disabled; // ‘boolean’ typeof button.getAttribute(‘disabled’); // ‘object’ button.disabled = true; typeof button.getAttribute(‘disabled’); // ‘string’
И теперь у нас названия, которые не совпадают…
div = document.createElement(‘div’); div.setAttribute(‘class’, ‘one’); div.className; // ‘one’ div.className = ‘two’; div.getAttribute(‘class’); // ‘two’
… и случаи, вообще не имеющие смысла:
input = document.createElement(‘input’); input.getAttribute(‘value’); // null input.value = ‘one’; input.getAttribute(‘value’); // null input.setAttribute(‘value’, ‘two’); input.value; // ‘one’
Но мы можем жить с этими тонкостями, потому что, конечно же, вещи будут теряться при переходе из строк (HTML) и DOM. Их ограниченное количество, они задокументированы, так что мы хотя бы можем выучить их, потратив некоторое количество времени и терпения.
Веб компонентв изменили это. Теперь не только нет никаких гарантий связи между аттрибутами и пропсами, но как автор веб компонента вы (вероятно) должны поддерживать оба. Что означает, что вы часто будете видеть такие вещи:
class MyThing extends HTMLElement < static get observedAttributes() < return [‘foo’, ‘bar’, ‘baz’]; >get foo() < return this.getAttribute(‘foo’); >set foo(value) < this.setAttribute(‘foo’, value); >get bar() < return this.getAttribute(‘bar’); >set bar(value) < this.setAttribute(‘bar’, value); >get baz() < return this.hasAttribute(‘baz’); >set baz(value) < if (value) < this.setAttribute(‘baz’, »); >else < this.removeAttribute(‘baz’); >> attributeChangedCallback(name, oldValue, newValue) < if (name === ‘foo’) < // . >if (name === ‘bar’) < // . >if (name === ‘baz’) < // . >> >
Иногда вы встретите противоположные вещи, attributeChangedCallback вызывает методы свойств. В любом случае, вид ужасный.
Фреймворки, например, имеют простой и однозначный способ ввести данные в компонент.
Спорный дизайн
Этот пункт немного более расплывчатый, но меня бесит, что attributeChangedCallback — это просто метод у элемента. Вы можете в прямом смысле сделать так:
const element = document.querySelector(‘my-thing’); element.attributeChangedCallback(‘w’, ‘t’, ‘f’);
Ни один из аттрибутов не изменился, но элемент будет вести себя, будто они сделали. Конечно же, Javascript всегда давал множество возможностей нечестной игры, но когда я вижу детали реализации, вылезающие таким образом, мне кажется, что это должно намекнуть нам, что с дизайном что-то не так.
DOM — это плохо
Окей, мы почти поняли, что DOM — это плохо. Но нам трудно представить, насколько это ужасный интерфейс для создания интерактивных приложений.
Пару месяцев назад я написал статью «Write less code», чтобы продемонстрировать, как Svelte позволяет вам делать веб компоненты более эффективно чем фреймворки вроде React или Vue. Но я не сравнивал с DOM, а должен был.
Чтобы исправить эту оплошность, вот у нас простой b=/> .
export let a; export let b; >
+ =
И это всё. Теперь давайте сделаем такое же с веб компонентами:
class Adder extends HTMLElement < constructor() < super(); this.attachShadow(< mode: ‘open’ >); this.shadowRoot.innerHTML = ` `; this.inputs = this.shadowRoot.querySelectorAll(‘input’); this.p = this.shadowRoot.querySelector(‘p’); this.update(); this.inputs[0].addEventListener(‘input’, e => < this.a = +e.target.value; >); this.inputs[1].addEventListener(‘input’, e => < this.b = +e.target.value; >); > static get observedAttributes() < return [‘a’, ‘b’]; >get a() < return +this.getAttribute(‘a’); >set a(value) < this.setAttribute(‘a’, value); >get b() < return +this.getAttribute(‘b’); >set b(value) < this.setAttribute(‘b’, value); >attributeChangedCallback() < this.update(); >update() < this.inputs[0].value = this.a; this.inputs[1].value = this.b; this.p.textContent = `$+ $ = $`; > > customElements.define(‘my-adder’, Adder);
Мда. Заметьте, что если вы замените a и b одновременно, то это вызовет две перерисовки. Фреймворки обычно не страдают от этой проблемы.
Глобальное пространство имен
Нам не нужно углубляться в это. Достаточно сказать, что опасность использования одного большого namespace понятна большинству разработчиков.
Это всё решенные проблемы
Наибольшая проблема всего этого в том, что у нас уже есть очень хорошие модели компонентов. Мы всё еще учимся, но основная проблема — синхронизация рендера с состоянием через манипуляции с DOM в компоненто-ориентированной модели — решена годы назад.
Но мы всё еще добавляем новые возможности платформе только чтобы довести web components до паритета с тем, что мы уже могли делать с помощью фреймворков.
Учитывая ограниченное количество ресурсов, время, потраченное на одну задачу, означает время не потраченное на другую. Большое количество усилий было приложено на разработку веб компонентов, несмотря на в основном безразличное отношение разработчиков. Чего мог бы достичь web, если бы эта энергия была бы потрачена где-либо еще?
Источник: sneakbug8.com
Заменят ли Web Components фреймворки?

Веб-разработка стремительно развивается и появляются новые виды различных инструментов разработки: библиотеки и фреймворки. Их появление обусловлено необходимостью быстрой и качественной разработки веб-приложений и получения результата, которого javascript в чистом виде не всегда способен предоставить. Но и реализация новых методов, которые могут сделать разработку эффективней, продолжает происходить. Благодаря этому, хоть и не такому быстрому развитию, как хотелось бы, появились специальные веб-компоненты, которые обещают помогать делать обычными средствами javascript и браузеров то, что раньше можно было сделать только подключив определенную библиотеку или использовав фреймворк.
Web Components (Веб-компоненты) — это технология позволяющая создавать компоненты в веб-документах и веб-приложениях для многократного использования.
Веб-компоненты частично поддерживаются в браузерах Chrome, Firefox, Opera и Safari и работают напрямую без необходимости подключать какие-либо библиотеки. В остальных, где нет поддержки веб-компонентов, используется полифилы.
Полифилы — это код, реализующий функционал, которые не поддерживается по умолчанию в некоторых браузерах. Полифил является специальной библиотекой, которая подключается для поддержки стандартов HTML 5.
Веб-компоненты включают 4 технологии:
- Custom Elements — API для создания собственных HTML элементов.
- HTML Templates — тег для реализации изолированные DOM-элементов
- Shadow DOM — изолирует DOM и стили в разных элементах.
- HTML Imports — импорт HTML документов.
Спецификация Web Components располагаются в GitHub репозитории webcomponents.
Спецификация веб-компонентов определяет следующие пункты:
- Новые элементы HTML:
- Связанные интерфейсы API для новых элементов: HTMLTemplateElement, HTMLContentElement (удален из спецификации) и HTMLShadowElement (удален из спецификации)
- Расширения для интерфейса HTMLLinkElement и
- API для регистрации встроенных элементов, Document.registerElement(), и модификации Document.createElement() и Document.createElementNS()
- Новый «жизненный цикл колбеков» может быть добавлен в прототип, на основе которого создается встроенный элемент
- Новый CSS псевдо-класс для задания стилей по умолчанию, :unresolved.
- Теневой DOM: ShadowRoot и Element.createShadowRoot(), Element.getDestinationInsertionPoints(), Element.shadowRoot
- Расширение для интерфейса Event, Event.path
- Расширение для интерфейса Document
- Для задания стилей Веб-компонент:
- Новые псевдо-классы: :host, :host(), :host-context()
- Новые псевдо-элементы: ::shadow и ::content
- Новый комбинатор, /deep/.
Custom Elements
Custom Elements (Пользовательские элементы) — эта технология позволяет создавать свои типы элементов, описывать для них свойства, методы и т.п.
Для регистрации нового элемента используется конструкция:
document.registerElement(name, < prototype: name_prototype >).
- name — имя нового элемента (тега). В названии обязательно должен быть дефис. Это сделано, чтобы предотвратить конфликт со стандартными HTML-элементами. Например, нальзя создать элемент footertop или footerTop, а название должно быть footer-top
- prototype — объект-прототип нового элемента, который должен наследовать от HTMLElement, чтобы у нового элемента были стандартные методы и свойства.
Возможно, непонятно, почему была создана новая возможность создания пользовательских элементов, поскольку уже можно создать имя тега, например, и стилизовать его с помощью CSS, а затем использовать скрипты для прикрепления к нему поведения.
Преимущество, которое имеют Custom Elements, — это их жизненные реакции , которые позволяют привязывать поведение к различным частям «жизненного цикла» нового элемента. Например, можно установить определенное поведение, когда элемент вставлен в DOM («подключен»), и другое поведение, когда оно удаляется из DOM («отключено») или когда его атрибуты меняются.
Методы Custom Elements
Пользовательские элементы имеют следующие методы, которые определяют, как они себя ведут:
- constructor() — вызывается, когда элемент создается или обновляется
- connectedCallback() — вызывается, когда элемент вставлен в документ, в том числе в теневое дерево
- disconnectedCallback() — вызывается, когда элемент удаляется из документа
- attributeChangedCallback(attributeName, oldValue, newValue, namespace) — вызывается, когда атрибут изменяется, добавляется, удаляется или заменяется элементом.
- adoptedCallback(oldDocument, newDocument) — вызывается, когда элемент принят в новый документ
Пример Custom Elements
// Create a class for the element class XProduct extends HTMLElement < constructor() < // Always call super first in constructor super(); // Create a shadow root var shadow = this.attachShadow(); // Create a standard img element and set its attributes. var img = document.createElement(‘img’); img.alt = this.getAttribute(‘data-name’); img.src = this.getAttribute(‘data-img’); img.width = ‘150’; img.height = ‘150’; img.className = ‘product-img’; // Add the image to the shadow root. shadow.appendChild(img); // Add an event listener to the image. img.addEventListener(‘click’, () => < window.location = this.getAttribute(‘data-url’); >); // Create a link to the product. var link = document.createElement(‘a’); link.innerText = this.getAttribute(‘data-name’); link.href = this.getAttribute(‘data-url’); link.className = ‘product-name’; // Add the link to the shadow root. shadow.appendChild(link); > > // Define the new element customElements.define(‘x-product’, XProduct);
body < background: #F7F7F7; >x-product < display: inline-block; float: left; margin: 0.5em; border-radius: 3px; background: #FFF; box-shadow: 0 1px 3px rgba(0,0,0,0.25); font-family: Helvetica, arial, sans-serif; -webkit-font-smoothing: antialiased; >x-product::slotted(.product-img) < cursor: pointer; background: #FFF; margin: 0.5em; >x-product::slotted(.product-name)
HTML Templates
HTML Templates — это отложенный рендер клиентского контента, который не отображается во время загрузки, но может быть инициализирован при помощи JavaScript. Парсер обрабатывает содержимое элемента во время загрузки страницы, только лишь чтобы убедиться в валидности содержимого, не отображая при этом само содержимое.
У элемента есть доступный только для чтения аттрибут content, предоставляющий доступ к содержимому шаблона. Проверив наличие этого атрибута помогает узнать, поддерживает ли браузет элемент .
Пример HTML Templates
// Проверяем поддерживает ли браузер тег // проверив наличие аттрибута content у элемента template if (‘content’ in document.createElement(‘template’)) < // Instantiate the table with the existing HTML tbody and the row with the template var t = document.querySelector(‘#productrow’), td = t.content.querySelectorAll(«td»); td[0].textContent = «1235646565»; td[1].textContent = «Stuff»; // клонируем новую строку и вставляем её в таблицу var tb = document.getElementsByTagName(«tbody»); var clone = document.importNode(t.content, true); tb[0].appendChild(clone); // создаём новую строку td[0].textContent = «0384928528»; td[1].textContent = «Acme Kidney Beans»; // клонируем новую строку и вставляем её в таблицу var clone2 = document.importNode(t.content, true); tb[0].appendChild(clone2); >else < // необходимо найти другой способ добавить строку в таблицу т.к. // тег не поддерживатся браузером >
Shadow DOM
Shadow DOM — это инкапсуляция для DOM и CSS в веб-компоненте и делает так, чтобы они оставались отдельно от DOM основного документа.
Суть инкапсуляции в том, чтобы сохранить код отдельно от остальной части страницы, когда, например, на большом сайте, не очень хорошо организован CSS и стили могут применяться в основной области содержимого, где этого делать не требуется, и наоборот.
Shadow DOM всегда должен быть привязан к существующему элементу: латеральному элементу в файле HTML, элементу созданному в DOM через js, либо пользовательским элементом.
Теневая DOM добавляется с помощью Element.attachShadow().
Пример Shadow DOM
// create shadow DOM on the
element above var shadow = document.querySelector(‘#hostElement’) .attachShadow();
shadow.innerHTML = ‘Here is some new text’;
// Create shadow DOM var shadow = document.querySelector(‘#hostElement’) .attachShadow(); // Add some text to shadow DOM shadow.innerHTML = ‘Here is some new text’; // Add some CSS to make the text red shadow.innerHTML += ‘span ‘;
Shadow DOM состоит из следующих частей:
- Element.attachShadow()
- Element.getDestinationInsertionPoints()
- Element.shadowRoot
- элемент
- Элемент
- Связанные интерфейсы API: ShadowRoot, HTMLTemplateElementиHTMLSlotElement
- Селекторы CSS:
- Псевдо-классы: :host, :host(),:host-context()
- Псевдо-элементы: ::slotted()
- Комбинатор: >>>*
Интерфейсы Shadow DOM
- ShadowRoot — корневой узел поддерева DOM, который отображается отдельно от основного дерева DOM документа.
- HTMLTemplateElement — включает доступ к содержимому HTML- элемента.
- HTMLSlotElement — включает доступ к имени и назначенным узлам элемента HTML .
- DocumentOrShadowRoot — предоставляет API, которые совместно используются документами и теневыми корнями.
HTML Imports
HTML Imports (HTML импорт) — это механизм упаковки для веб-компонентов, но его также можно использовать сам по себе.
Импорт производится с помощью тега :
Полноценные веб-компоненты будут не скоро. Кроме этого, как и все остальное в javascript, в чистом виде редко используется и применяются различные фреймворки и библиотеки, являющиеся обертками вокруг этих чистых методов для более простого использования.
Так что, можно ожидать, что веб-компоненты не заменят фреймворки и библиотеки, а войдут в их текущий функционал и будут сосуществовать наравне с ними. По крайней мере, чтобы заменить фреймворки им придется потягаться с ними. Да и, в общем-то, сами стандарты веб-компоненто, еще не до конца определены.
Как вы думаете, заменят ли Web Components фреймворки?
Источник: unetway.com