Автор оригинала: Benny Powers.
Этот пост изначально опубликован на dev.to
Компонент ui – это все в наши дни. Знаете ли вы, что в Интернете есть свой собственный модуль нативного компонента, который не требует использования любых библиотек? Правдивая история! Вы можете написать, опубликовать и повторно использовать одно файловые компоненты, которые будут работать в любом * хороший браузер и В любых рамках (Если это ваша сумка).
В нашем Последнее сообщение Мы узнали о двухбиликах JavaScript, которые позволяют нам отправлять компоненты для браузеров, которые не поддерживают спецификации.
Сегодня мы получаем практику 👷♂️, мы создадим одно файловый веб-компонент без какой-либо библиотеки или рамочного кода. Мы собираемся написать элемент, который ленивый – загружает изображения, чтобы браузер только вытеснил только тогда, когда они появляются (или собираются появляться) на экране. Мы сделаем наш элемент доступны и кредитные плеча API S Arke ПересечениеОбсеревер
Чтобы сделать это легкий и исполнительные средства Отказ Мы могли бы даже добавить некоторые дополнительные колокольчики и свистки, если мы чувствуем себя как это.
- Пользовательский элемент класса
- Обратные вызовы жизненного цикла
- Конструктор
- ConnectedCallback
- AttributeChangedCallback.
- ОтключенныйКоллбежок
- УсыновленныйCallback.
- Жизненный цикл страницы
- Ленивая загрузка
- Укладка нашего компонента
- : хост и <слот>
- CSS пользовательские свойства
- Доступность
- Расширение встроенных элементов
- Доступные автономные элементы
- Выводы
Давайте начнем! Откройте свой редактор и создайте файл под названием Lazy-Image.js
Этот файл будет содержать наш компонент.
Пользовательский элемент класса
Также как мы видели в нашем первом посте веб-компонентов стандартов веб-компонентов, наш первый шаг будет инициализировать и зарегистрировать пользовательский класс элемента и предоставлять его базовым шаблоном. Позже мы улучшимся на шаблоне, добавив наше таможенное поведение.
const tagName = 'lazy-image'; const template = document.createElement('template'); template.innerHTML = ``; class LazyImage extends HTMLElement { connectedCallback() { if (!this.shadowRoot) { this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(template.content.cloneNode(true)); } } } const register = () => customElements.define(tagName, LazyImage); window.WebComponents ? window.WebComponents.waitFor(register) : register();
Хорошо. Если вы следили с нашими предыдущими сообщениями, все должны казаться знакомыми, но небольшой обзор в порядке:
- Мы создаем элемент шаблона и определите тень нашего элемента DOM внутри него.
- Определим наш поведение пользовательского элемента в
Класс
Отказ - Наш элемент
ConnectedCallback
Способ создает корень тени и маркирует шаблон в него.
PLOP, что в ваш документ и Giv’er:
Проверьте пример в https://glitch.me/sunset-sink
Увлекательно, верно? Хорошо, это скромное начало, но, по крайней мере, это работает. Если мы осмотрим наш элемент с инструментами DEV, мы можем видеть, что он содержит нашу Shadow DOM и связан с нашим пользовательским классом элемента.
Что мало Пользовательские
Значок – это путь Firefox рассказать нам, что это пользовательский элемент. Если вы нажмете на значок, отладчик будет открыт на определении вашего элемента. Хорошо сделано, Firefox DEV Tools Tool!
В следующем разделе мы действительно начнем готовить.
Обратные вызовы жизненного цикла
Пользовательские элементы имеют четыре специальных метода экземпляра, которые будут работать в разное время:
- ConnectedCallback ,
- AttributeChangedCallback ,
- Отключенcallback ,
- УсыновлениеCallback ,
Все определено как null
по умолчанию. Они, как и Конструктор Выполняет обратные вызовы жизненного цикла пользовательского элемента.
Конструктор
Первый из них является конструктором. Он работает всякий раз, когда элемент создан, до того, как элемент подключен к документу.
// CustomElement's constructor runs const el = document.createElement('custom-element');
Конструктор пользовательского элемента не должен иметь никаких параметров, и он должен позвонить Super ()
на первой строке своего тела, чтобы делегировать поведение на Htmlelement
, Узел
, так далее.; и связать это
к экземпляру элемента. Конструктор не должен возвращать какое-либо значение, кроме undefined
или это
;
// Don't do this class BustedElement extends HTMLElement { constructor(bar) { this.foo = bar; return bar; } } // Do This class DecentElement extends HTMLElement { constructor() { super(); if (!window.bar) return; this.foo = window.bar; } }
Возможно, вы захотите получить доступ к атрибутам вашего элемента Parentnode
, дети и т. Д. В конструкторе, но не дайте искушению: если ваш элемент не подключен (то есть прилагается) к дереву DOM, он пока не будет обновлен, то есть у него еще нет детей или атрибуты. Ваш код будет работать в случае, когда элемент уже определен в документе, прежде чем элемент определен, но не удается в случае, когда JavaScript создает элемент.
Также хорошо прикрепить корень тени в конструкторе и добавить к нему элементы. Но так как полифиллики должны добавлять классы в свет DOM, а элемент, возможно, еще не подключился, мы будем делать это на протяжении всего этого руководства в ConnectedCallback
,
По этим причинам лучше всего ограничивать активность конструктора для настройки внутреннего состояния, включая значения по умолчанию, а также при использовании полифиллирования, чтобы прикрепить корень тени и вызов Стильэмент
в ConnectedCallback
Отказ Просто обязательно проверить, если ShadowRoot
Уже существует, или ошибка выкисляет в следующий раз, когда ваш элемент подключается (например, через Document.body.append (MyLazyImage)
).
// Don't do this class BustedImage extends HTMLElement { constructor() { super(); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(template.content.cloneNode(true)); this.shadowImage = this.shadowRoot.getElementById('image'); // OOPS! Light DOM attributes may not yet exist! this.shadowImage.src = this.getAttribute('src'); } } // Do This class LazyImage extends HTMLElement { constructor() { super(); // Set default values of properties, as needed. this.src = ''; // In order to work well with the polyfill, // We'll set up the DOM later on, when the element connects. } }
ConnectedCallback
ConnectedCallback
Уволен каждый раз, когда ваш элемент подключается к DOM, включая первый раз, который он обновлен. Это подходящий момент, чтобы настроить теневые дети и атрибуты.
const lazyImage = document.createElement('lazy-image'); // constructor runs document.appendChild(lazyImage); // connectedCallback runs const container = document.getElementById('container'); container.appendChild(lazyImage); // connectedCallback runs again
class LazyImage extends HTMLElement { constructor() { super(); this.src = ''; this.alt = ''; } connectedCallback() { // Initialize properties that depend on light DOM this.src = this.getAttribute('src') || this.src; this.alt = this.getAttribute('alt') || this.alt; // Check if shadowRoot exists first if (!this.shadowRoot) { this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(template.content.cloneNode(true)); this.shadowImage = this.shadowRoot.getElementById('image') } // Set the shadow img attributes. this.shadowImage.src = this.src; this.shadowImage.alt = this.alt; } }
Проверьте пример в https://glitch.me/adaptable-order
Ну, это обнадеживает. Мы создали нашу Shadow DOM и сделали некоторые основные сантехники, которые устанавливают нашу внутреннюю IMG
Элемент SRC
и Alt
Атрибуты в соответствии с теми, которые нашли на нашем элементе, когда он был обновлен.
Мы хотим нашего ShadowImage
‘s SRC
Атрибут, чтобы быть синхронизированным с нашими элементами, и мы также хотим синхронизировать эти атрибуты с SRC
Дом недвижимости. С помощью AttributeChangedCallback
И некоторые укладыши, мы сделаем это случиться.
AttributeChangedCallback.
Когда вы меняете SRC
атрибут простой Элемент, браузер отвечает путем выбора и отображения нового изображения URL . Точно так же, когда вы используете JavaScript для установки
SRC
Свойство на объекте DOM элемента, новое значение отражено в атрибуте. Мы хотим, чтобы наш элемент вести себя так же. Спецификация html предоставляет AttributeChangedCallback
Для таких видов использования.
В любое время атрибуты вашего элемента меняются, обратный вызов будет работать с именем атрибута, старым значелем и новым значением в качестве аргументов. Но браузер не будет соблюдать только какие-либо атрибуты. Вы должны заранее указать, какие атрибуты вы хотите отреагировать, определив список имен атрибутов в статическом свойстве, называемом Наблюдательные материалы
:
static get observedAttributes() { return ['src', 'alt']; }
С этим определенным, ваш элемент AttributeChangedCallback
будет работать всякий раз, когда любой из SRC
или Alt
Изменение атрибутов. На данный момент мы будем просто прямыми значениями как свойства.
attributeChangedCallback(name, oldVal, newVal) { this[name] = newVal }
Мы также хотим, чтобы наш элемент реагировать на изменения имущества, обновляя его ShadowImage, и, отражая новое значение для атрибута. Мы будем использовать загадки для этого:
class LazyImage extends HTMLElement { /** * Guards against loops when reflecting observed attributes. * @param {String} name Attribute name * @param {any} value * @protected */ safeSetAttribute(name, value) { if (this.getAttribute(name) !== value) this.setAttribute(name, value); } /** * Image URI. * @type {String} */ set src(value) { this.safeSetAttribute('src', value); // Set image src if (this.shadowImage) this.shadowImage.src = value; } get src() { return this.getAttribute('src') } /** * Image Alt tag. * @type {String} */ set alt(value) { this.safeSetAttribute('alt', value); // Set image alt if (this.shadowImage) this.shadowImage.alt = value; } get alt() { return this.getAttribute('alt') } static get observedAttributes() { return ['src', 'alt']; } connectedCallback() { this.src = this.getAttribute('src'); this.alt = this.getAttribute('alt'); if (!this.shadowRoot) { this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(template.content.cloneNode(true)); this.shadowImage = this.shadowRoot.getElementById('image'); } } attributeChangedCallback(name, oldVal, newVal) { this[name] = newVal; } }
Проверьте пример в https://glitch.me/guttural-tarsier
Нажатие кнопки обновления SRC
и Alt
Свойства и атрибуты на пользовательском элементе, а также это тени для ребенка.
Наш элемент теперь прозрачно выставляет основную функциональность родных элемент. Следующим шагом является добавление в нашу функцию Lazy-loading. Но прежде чем мы сделаем это, давайте кратко обсудим последние два обратных вызова в жизненным цитам в спецификации.
ОтключенныйКоллбежок
Всякий раз, когда ваш элемент должен сделать любую работу, прежде чем удалить из DOM, определите Отключенcallback
Это обрабатывает вашу уборку.
disconnectedCallback() { /* do cleanup stuff here */ }
Это будет удобно для нас позже, когда мы создаем ПересечениеОбсеревер
Для каждого экземпляра нашего элемента. На данный момент мы оставим это как заглушку.
УсыновленныйCallback.
Пользовательские элементы также имеют УсыновлениеCallback
который работает всякий раз, когда вы звоните Утверждать
на пользовательском элементе, который внутри другого документа или фрагмента документа. В этом случае сначала элемент Отключенcallback
будет работать, когда он отключается от своего оригинального документа, то УсыновлениеCallback
и, наконец, ConnectedCallback
Когда он подключается к вашему документу.
Я думаю, что это было в основном предназначено для несуществующего HTML Imports Spec. Это вполне может стать более актуальным, если либо HTML модули Предложения приняты. Если у вас есть какие-либо идеи для использования случаев, мы увидимся в разделе комментариев.
Жизненный цикл страницы
Таким образом, ваша страница жизненного цикла может выглядеть что-то подобное:
- Получить критические ресурсы, в том числе полифилл
- Построить домо
- Fetch отсроченные сценарии и модули, в том числе
lazy-image.js.
- Domcontentloaded – Документ закончен разбор
- Polyfills Finish Setup,
WebComponents.waitfor
вызывает свой обратный вызов - Пользовательские элементы обновлены – каждый экземпляр
В документе обновляется до пользовательского элемента.Конструктор
иConnectedCallback
запустить. - Если JavaScript создает экземпляр
Конструктор будет работать. Когда экземпляр подключен к дереву DOM,ConnectedCallback
будет работать. - Если JavaScript удаляет экземпляр
от DOM,Отключенcallback
будет работать.
Ленивая загрузка
Мы будем использовать ПересечениеОбсеревер
API для ленивой загрузки. Когда изображение пересекается с прямоугольником, немного больше, чем на экране, мы начнем загрузку, и, надеюсь, он будет полностью загружен тем временем, когда изображение прокручивается на вид. ConnectedCallback
так же хорошее место, как любое, чтобы сделать эту работу.
Во-первых, давайте определим быстрый предикат у корня объема нашего модуля:
// isIntersecting :: IntersectionObserverEntry -> Boolean const isIntersecting = ({isIntersecting}) => isIntersecting
Тогда мы можем настроить наблюдатель, когда наши элементы инстанции:
constructor() { super(); // Bind the observerCallback so it can access the element with `this`. this.observerCallback = this.observerCallback.bind(this); } connectedCallback() { // initialize pre-upgrade attributes this.src = this.getAttribute('src') this.alt = this.getAttribute('alt') // Set up shadow root. if (!this.shadowRoot) { this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(template.content.cloneNode(true)); this.shadowImage = this.shadowRoot.getElementById('image'); } // If IntersectionObserver is available, initialize it. // otherwise, simply load the image. if ('IntersectionObserver' in window) this.initIntersectionObserver() else this.intersecting = true } /** * Sets the `intersecting` property when the element is on screen. * @param {[IntersectionObserverEntry]} entries * @protected */ observerCallback(entries) { // The observer simply sets a property if (entries.some(isIntersecting)) this.intersecting = true } /** * Initializes the IntersectionObserver when the element instantiates. * @protected */ initIntersectionObserver() { if (this.observer) return; // Start loading the image 10px before it appears on screen const rootMargin = '10px'; this.observer = new IntersectionObserver(this.observerCallback, { rootMargin }); this.observer.observe(this); }
Когда наблюдатель триггеры и устанавливают пересекание
Свойство, давайте отражем его как атрибут и начните загружать изображение. Поскольку этот наблюдатель должен только один раз, мы можем отключить и разгрузить его, как только это сделано.
/** * Whether the element is on screen. * @type {Boolean} */ set intersecting(value) { if (value) { this.shadowImage.src = this.src; this.setAttribute('intersecting', ''); this.disconnectObserver(); } else { this.removeAttribute('intersecting') } } get intersecting() { return this.hasAttribute('intersecting') } /** * Disconnects and unloads the IntersectionObserver. * @protected */ disconnectObserver() { this.observer.disconnect(); this.observer = null; delete this.observer; }
Мы захочем разгрузить наш наблюдатель, если элемент удаляется из DOM, в противном случае мы можем утерить память. Мы можем использовать Отключенcallback
для этого.
disconnectedCallback() { this.disconnectObserver() }
Укладка нашего компонента
Теперь у нас достаточно, чтобы лениво загрузить наше изображение, как только он появится на экране, но мы хотим, например, наш элемент также предоставить ux ux , например, загрузки изображения зажигания заполнения. Для этого мы стимулируем наш компонент, добавив <СТИЛЬ>
тег в тень нашего элемента.
const tagName = 'lazy-image'; const template = document.createElement('template'); template.innerHTML = ``; window.ShadyCSS && window.ShadyCSS.prepareTemplate(template, tagName);
: хост и <слот>
Оооо! Новый Груз ! : хозяин
Селектор CSS : хозяин ([пересекание])
который эквивалентен ленивое изображение [пересекание]
, если он был выбран из-за пределов теневого корня.
<Слот>
Элемент, и это связано :: leded ()
Функция CSS – это части спецификации, которые давайте передаем биты DOM от светового дерева в теневое дерево. Вы используете <Слот>
Внутри тени, как мы видели чуть выше. Тогда вы передаете содержимое от света, как теневое дерево, как так:
Уведомление здесь, как мы имели в виду Ограничения полифилл и завернул наши <Слот>
в оформлении
затем выбран для детей этого
в наших CSS.
<Слот>
На самом деле на самом деле не перемещается или не добавляет щелевые элементы, он просто отображает их, как будто они были в корне тени. Таким образом, стили, которые применяются к удаленным содержанием от внешнего документа, все равно будут применяться при прорезве. Ваш элемент может добавить свои собственные стили, чтобы подрезать контент с помощью :: leded ()
CSS функция.
::slotted(svg) { /* applies to any slotted svg element */ } ::slotted(img) { /* applies to any slotted img element */ }
Обратите внимание хорошо : :: leded (*)
Выбирает для Только элементы , не текстовые узлы. Это также выбирает только для узлов верхнего уровня, а не детей:
/* Don't do this */ .wrapper ::slotted(.outer .inner) { /*...*/ } .wrapper ::slotted(.inner) { /*...*/ } /* Do this */ .wrapper ::slotted(.outer) { /*...*/ }
Это оптимизация производительности браузера, и ее можно раздражать, чтобы работать в некоторых случаях, но с творческой работой DOM и умным приложением факторинга, оно может быть решено.
Слоты могут быть названы или анонимными. Назовите слоту, давая ему name = "slotname"
атрибут в Shadow DOM и используйте его, указав
в светом доме. Именные слоты полезны, если вы хотите предоставить несколько конкретных настраиваемых функций. В нашем случае мы используем название <Слот>
ради яплицерации, но мы могли бы так же легко использовать анонимную <Слот>
Отказ
I'm the article title
I'm the article content
I get slotted into the anonymous slot, too
Теперь, когда мы передали нашу светлое заполнителю Дом в нашу тени, давайте обновим методы нашего класса для обработки заполнителя:
set intersecting(value) { if (value) { // Wait to apply the `intersecting` attribute until the image // finishes loading, then update the styles for polyfill browsers this.shadowImage.onload = this.setIntersecting; this.shadowImage.src = this.src; this.disconnectObserver(); } else { this.removeAttribute('intersecting'); } } constructor() { super(); this.setIntersecting = this.setIntersecting.bind(this); } /** * Sets the intersecting attribute and reload styles if the polyfill is at play. * @protected */ setIntersecting() { this.setAttribute('intersecting', ''); this.updateShadyStyles(); } connectedCallback() { this.updateShadyStyles(); /* etc. */ } /** * When the polyfill is at play, ensure that styles are updated. * @protected */ updateShadyStyles() { window.ShadyCSS && window.ShadyCSS.styleElement(this); }
Проверьте пример в https://glitch.me/abalone-mongoose
😎 Ницца! Наш автономный, многоразовый, одноразовый пользовательский элемент настраивает изображение, когда на экране затем исчезает к нему из щелевого заполнителя.
<В отставке> Кстати, это отличная возможность увидеть, как разбиваются полифиллики близко. Если вы загружаете эту страницу в поддерживающем браузере, вы увидите тег в стиле в теневом дереве элемента, но если вы загружаете его на полифиллированном браузере, как Edge или Firefox 62, вы не увидите стилей, потому что Sadycss Polyfill Поднимает стили тени до головы документа.
CSS пользовательские свойства
Shadow DOM поддерживает наши стили, выделенные от остальной части документа, но это означает, что для наших пользователей сложнее настраивать наш компонент. Счастливых для нас, CSS пользовательские свойства пронзили границу тени, поэтому мы можем использовать их для выставления настраиваемых стилей на наших элементах.
Мы сделаем это, просто определяя наши стили с помощью пользовательских свойств. Синтаксис пользовательских свойств позволяет использовать переменные объявления при назначении значений по умолчанию:
.selector { rule: var(--custom-property-name, default); }
Таким образом, мы можем стимулировать наш элемент разумным элементом по умолчанию, при этом именем пользователю некоторую гибкость:
#image, #placeholder ::slotted(*) { position: absolute; top: 0; left: 0; transition: opacity var(--lazy-image-fade-duration, 0.3s) var(--lazy-image-fade-easing, ease); object-fit: var(--lazy-image-fit, contain); width: var(--lazy-image-width, 100%); height: var(--lazy-image-height, 100%); }
Затем мы можем настроить эти стили во всем мире, либо на определенный элемент, определяя эти переменные в наших стилях документа:
/* applies to the whole document. */ html { --lazy-image-width: 400px; --lazy-image-height: 200px; } /* applies to specific elements */ lazy-image:last-of-type { width: 400px; height: 200px; --lazy-image-width: 100%; --lazy-image-height: 100%; --lazy-image-fade-duration: 2s; --lazy-image-fade-easing: linear; }
Доступность
Прежде чем публиковать наш компонент, давайте убедитесь, что он относится к всем нашим пользователям с уважением. Вы не будете служить вкусным навязанным на гриль-ребрах (кто-то еще голоден?) Без обрезки излишки висит на битах и крутине. Никто не хочет жевать это! Давайте обремем жир от нашего компонента дерево A11Y Спецификация пользовательских элементов предусматривает Настройка встроенных элементов Отказ Для справки, настроенные встроенные элементы выглядят так: Это выглядит потрясающе и решит так много проблем, связанных с доступностью, но Официальная позиция Apple на сегодняшний день Это то, что они не будут реализовать его, поэтому мы будем писать автономные пользовательские элементы на время. Поскольку наша компонент обернет Далее мы настроим И последнее, мы поменяем Теперь наше дерево A11Y – это приятный и аккуратный, пользователи нашего экрана читателя не будут беспокоиться с посторонним домом. Проверьте пример в https://glitch.me/cream-art Убийца. Вот наш полный модуль: Вы можете использовать Взносы Добро пожаловать на Github Отказ Мы достигли нашей цели на написание Slick, многоразовая, доступная, без зависимостей, одно файловый компонент загрузки беззаконного загрузки. И это всего 1,94 КБ сжатый, 4,50 кБ. Что мы узнали? Есть определенно достоинства и недостатки, чтобы катиться своим собственным. Похоже, что мы можем примерно урегулировать в этом общем правиле: если вы создаете простой, многоразовый, независимый пользовательский элемент для раскрытия определенной конкретной функциональности; Ваниль – это прекрасный выбор; Но для более крупных проектов и команд преимущества библиотеки (готовые или на заказ) быстро накапливаются. Одно следует рассмотреть, это то, что некоторые рамки обеспечивают единообразность. В некоторых командах это преимущество, однако, что модель компонентов позволяет разбить команды независимо работать независимо на небольших абстракциях, скрываясь в этих видах деталей реализации из большей команды. В любом крупном проекте подобные вещи должны будут рассмотреть при выборе подходящего уровня абстракции для получения компонента или набора компонентов. В наших следующих нескольких сообщениях мы будем изучать некоторые библиотеки, инструменты и стратегии оптимизации, которые могут оптимизировать ваш процесс разработки веб-компонента и производительность приложений. И мы начинаем с Библиотека веб-компонентов: полимер. Увидимся тогда 🕵️♂️🕵️♀️ Хотели бы вы сеанс наставничества на одну на одну на одну из темов, покрытых здесь? Спасибо без особых приказа Джоне Пакеру, Уэстбрулу Джонсону, Гофферту Ван Гулю, Мэтту Гаварецким и Даниэлем Тернер для их предложений и исправлений.Расширение встроенных элементов
Доступные автономные элементы
Элемент, вместо того, чтобы расширять его, мы должны попытаться сделать всю нашу оберточную дому прозрачным для считывателей экрана. Сначала мы обновим нашу начальную разметку, чтобы заполнитель был показан деревом A11Y, но не изображение.
Презентация
Роль, так что обертка нашего элемента игнорируется в пользу его содержимого Screeneaders.connectedCallback() {
// Remove the wrapping `
Aria-Hidden
Атрибуты на нашем теневом изображении и заполнятелях после нагрузки изображения.setIntersecting() {
/* etc. */
this.shadowImage.setAttribute('aria-hidden', 'false')
this.shadowPlaceholder.setAttribute('aria-hidden', 'true')
}
const isIntersecting = ({isIntersecting}) => isIntersecting;
const tagName = 'lazy-image';
const template = document.createElement('template');
template.innerHTML = `
В ваших проектах, установив из NPM или погрузка от Unpkg Отказnpm i -S @power-elements/lazy-image
Выводы
Ванильные компоненты плюсы и минусы
Вам нужно будет предоставить свои собственные помощники. Синхронизация свойств с атрибутами может стать громоздкой.
Никаких зависимостей не требуется. Ваш код является будущим доказательством, потому что он опирается на веб-стандарты вместо библиотеки Churn.
Компоненты 0-DEP не используют библиотеки Mixins или Helper для уменьшения файлов в крупных проектах.
Небольшой след загрузки, поскольку не требуются дополнительные круглые переговоры для библиотечного кода
Низкоуровневые веб-примитивы иногда могут быть громоздкими.
Нет нестандартных API для изучения, обслуживания или адаптации к. Это просто в Интернете.
Вы должны выйти из своего пути для поддержки браузеров Polyfill, тогда как с библиотеками, ограничениями полифилл и известные проблемы абстрагированы.
Низкоуровневая мощность дает вам контроль и гибкость. Вы можете учитывать ваши компоненты, однако вы хотите.
Признательности
Ошибка