Кастомные элементы без зависимостей
Тред из второго дня моей недели в @jsunderhood.
Тред про кастомные элементы без зависимостей, с примерами из личного опыта. И заодно немного о best practices.
— jsunderhood (@jsunderhood) November 10, 2020
Кастомные элементы без зависимостей писали еще 5 лет назад, на “версии 0”. Вот, например, популярный в то время компонент роутера (и один из первых проектов на GitHub, куда я контрибьютил).https://t.co/Wv0a03BJsK
— jsunderhood (@jsunderhood) November 10, 2020
Правда, долгое время была одна неявная зависимость: полифилл. Его нужно было подключать на страницу отдельным скриптом, размер которого в среднем составлял 5-6 KB (min + gzip).https://t.co/rU9WMwmZwk
— jsunderhood (@jsunderhood) November 10, 2020
Кроме “официального” полифилла от разработчиков Google, был еще document-register-element от Andrea Giammarchi. Он использовался в Angular и AMP. На данный момент deprecated и уже архивирован.https://t.co/PkbIOQxqeW
— jsunderhood (@jsunderhood) November 10, 2020
Еще один момент: кастомные элементы требуют нативных ES2015-классов. Это тоже своего рода зависимость. ES5-код, полученный с помощью Babel, не будет работать с customElements.define.
— jsunderhood (@jsunderhood) November 10, 2020
Решается эта проблема с помощью с помощью манки-патчинга HTMLElement с добавлением вызова Reflect.construct. В составе “официального” полифилла для этого есть отдельный скрипт.https://t.co/nxO3VmwUUy
— jsunderhood (@jsunderhood) November 10, 2020
По вышеуказанной причине в сообществе веб-компонентов принято публиковать в npm исходный код кастомных элементов в ES2015. Примеры, которые будут в этом треде, следуют этому правилу.
— jsunderhood (@jsunderhood) November 10, 2020
Итак, рассказываю о двух своих недавних пет-проектах. Это “клоны” React-компонентов, написанные на ванильных кастомных элементах без зависимостей. Вся эта история началась с твита:https://t.co/XGi5j4Dxhw
— jsunderhood (@jsunderhood) November 10, 2020
Так появился проект vanilla-colorful, порт колор-пикера react-colorful от @Omgovich (если пропустили его неделю, обязательно почитайте).https://t.co/AI7EkMxyKL
— jsunderhood (@jsunderhood) November 10, 2020
От идеи до реализации прошло всего несколько дней. Замечу, что опыта разработки на React у меня нет. Исходный код react-colorful хорошо оформлен и снабжен комментариями, это очень помогло.https://t.co/Zcxalu2PPz
— jsunderhood (@jsunderhood) November 10, 2020
Подробнее можно узнать в 248 выпуске подкаста Веб-стандарты, где мы с @Omgovich обсуждали наши колор-пикеры и сравнивали плюсы и минусы используемых подходов.https://t.co/qIDRyPnNIz
— jsunderhood (@jsunderhood) November 10, 2020
Второй мой проект — vanilla-hamburger: набор гамбургер-кнопок с разными анимациями, порт библиотеки hamburger-react. В отличие от оригинала, под капотом использует <button>, а не <div>.https://t.co/V84J69a26p
— jsunderhood (@jsunderhood) November 10, 2020
Оба проекта написаны на TypeScript и покрыты тестами. Об этом, пожалуй, сделаю чуть позже отдельный тред. А пока несколько слов о подходах, использованных при написании этих компонентов.
— jsunderhood (@jsunderhood) November 10, 2020
Как и стандартные HTML элементы, кастомный элемент может иметь атрибуты и свойства. По умолчанию они не связаны между собой. В качестве решения обычно используется пара геттер-сеттер.https://t.co/x2MHSydYbQ pic.twitter.com/KsHgLHTYup
— jsunderhood (@jsunderhood) November 10, 2020
В этом примере я использую Symbol, чтобы эмулировать приватное поле. Это полезно, поскольку иначе в автокомплите и браузерных DevTools будет много лишнего. Подробнее в статье Jan Miksovsky:https://t.co/mr3h6uIcUu
— jsunderhood (@jsunderhood) November 10, 2020
В сеттерах свойств принято вызывать сайд-эффекты (например, обновлять DOM). Значения по умолчанию задаются в constructor, это позволяет переопределять их с помощью атрибутов.https://t.co/fajshARu6x pic.twitter.com/JqfZBfR6Xv
— jsunderhood (@jsunderhood) November 10, 2020
Свойство может быть установлено до того, как элемент связан с классом. При этом геттер и сеттер будут "затенены" (shadowed). Чтобы это исправить, вызываем сеттер в connectedCallback.https://t.co/H9pxNxEJrj pic.twitter.com/lQrpYuzqbp
— jsunderhood (@jsunderhood) November 10, 2020
С помощью пары строк кода реализуем простейший асинхронный рендеринг. Этот подход (batch updates) позволяет обновлять DOM один раз за микротаск при изменении нескольких свойств.https://t.co/O32JLcj6jc pic.twitter.com/gokUzbJJCY
— jsunderhood (@jsunderhood) November 10, 2020
Насчет сравнения с “Angular первых версий” — на самом деле, намного больше все это похоже на lit-element. Если точнее, его базовый класс UpdatingElement, который не требует lit-html.https://t.co/cXNHIlmyRq
— jsunderhood (@jsunderhood) November 10, 2020
Еще один интересный прием — встроенный метод handleEvent для обработчиков событий. Он позволяет обойтись без использования .bind(this) или стрелочных функций в addEventListener.https://t.co/Xmbr3irqb9 pic.twitter.com/lLH15nfbxL
— jsunderhood (@jsunderhood) November 10, 2020
Некоторые из описанных приемов можно увидеть в проекте HowTo: Components от авторов Web Fundamentals. Он не обновлялся три года, но до сих пор актуален — одно из преимуществ стандартов.https://t.co/8lr5yXU4P6
— jsunderhood (@jsunderhood) November 10, 2020
Также на Web Fundamentals есть список best practices для кастомных элементов. Там достаточно подробно описаны атрибуты и свойства (и как при работе с ними избежать бесконечного цикла).https://t.co/0L7jE7WdBO
— jsunderhood (@jsunderhood) November 10, 2020
В качестве более обзорного чтения советую Gold Standard Checklist от Jan Miksovky, на чьи работы я часто ссылаюсь. Он содержит ряд требований к API веб-компонентов, доступности и многое другое.https://t.co/f7NCZ3EDsO
— jsunderhood (@jsunderhood) November 10, 2020
И в завершение треда — еще один чеклист от Justin Fagnani о том, как публиковать кастомные элементы в npm. Есть полезные советы о структуре пакетов, важные поля package.json и так далее.https://t.co/mshN8ix5iS
— jsunderhood (@jsunderhood) November 10, 2020