Комментарии 27
Вэб компоненты сложны и многословны. Классы более сложные и менее гибкие абстракции чем функции (как например в Реакт). Создавать и обновлять ДОМ вручную тоже не легкое занятие если это не один единственный элемент. Но Реакт слишком много пытается на себя взять для библиотеки: стейт, асинхронность, исключения, компиляция, сервер... Предлягаю посмотреть легкую библиотеку где из Реакта взято только создание и обновление ДОМ. Ее можно использовать как отдельно, так и внутри Вэб Компонентов для управлени ДОМ.
Честно говоря, я не понимаю почему в html просто не сделали, чтобы каждый документ мог использовать произвольные теги, которые потом определять через css. И для совместимости иметь стандартный сет, чей css был бы предопределен браузером. Ведь, без JS, то что описано в статье работать не будет...
div
да сделали, со времен ie7 еще все это работает без js - https://habr.com/ru/articles/819465/
без JS, то что описано в статье работать не будет...
И не должно. Веб-компоненты они как-бы для SPA/PWA.
В них же нет ничего нового, то же самое можно воспроизвести на div-ах. Разница в том, что если вы сделаете такой-же компонент аватара на дивах, и будете отдавать его в html с сервера, то если в html будет список из 1000 аватаров... ну вы поняли.
С веб компонентами у нас есть функция для создания такого аватара. Сколько раз ее вызвать уже ваше дело.
То есть, github — это по-вашему SPA/PWA? Они с 2014 года веб-компоненты используют.
А может объяснить в чем прикол экспортировать функцию customElements.define
потом дожидаться domload и только потом регистрировать компоненты?
Почему бы не вызвать define прямо в файле с компонентом - типа если файл импортирован - то компонент нужен.
И для чего регистрировать компоненты после domload почему нельзя до ?
Кажется какие-то лишние телодвижения , которые и размер кода увеличивают и время инициализации без видимых преимуществ.
Потому что компоненты участвую в иерархии DOM , их просто негде ещё инициализировать
Думаю, потому, что используется тег скрипт без type=module, а в коде самого компонента есть обращение к document
там обращение к document идет не в contructor класса, а начиная с connectedCallback - который вызовется только после загрузки dom
короче можно писать прям так
import './sub-component'
customElements.define('my-component', class extends HTMLElement {
connectedCallback(){
this.innerHTML = '<sub-component></sub-component>'
}
})
и не морочится с порядком их регистрации
весь index.js
из статьи - просто лишний код
Есть еще один способ передавать данные в компонент - через его дочерние элементы
```<custom-select>
<option value="v1"> item 1</option>
...
</custom-select>```
В момент инициализации своего компонента - парсите вложенные в него теги. В принципе так что угодно в рамках разумного, хоть json можно передать в <code style="display:none">{ myjson: { a:1 }}</code> если ситуация позволяет.
Интересно, насколько всё это тормозит по сравнению с настоящим HTML/CSS?
Пользуюсь во всю, причем в реакте. Никаких тормозов, больше того, веб-компонент существенно производительнее чем тоже самое но на композиции из реакт элементов
Так вы не с тем сравниваете. Сравнивать надо с обычной вёрсткой. Без JS'а и реакта.
а на обычной верстке это не будет работать, с чем сравниваем ? тормоза будут там где вы добавили connectedCallback
, вот насколько они будут тормозить. настолько и будут тормознее, магии нет, это по прежнему DOM классы и наследованы прототипно (а да ещё есть мааленький оверхед на +1 поиск по прототипу) больше тормозов там неоткуда взяться.
Просто поделюсь своими мыслями. В свое время перегорел к веб-разработке из-за необходимости поддержки работы CSS, JS и HTML кода одновременно под Trident, Presto, Gecko, WebKit и-что-еще-там-было - когда работоспособность в чем-то одном могла означать полную неработоспособность в чем-то другом, а когда заставлял работать во втором, оно переставало работать в третьем. Сейчас, конечно, с этим намного проще, но вся эта современная спешка из разряда "чем быстрее релиз, тем больше денег, а оптимизация потом", а также, всё, что ей способствует, и из неё вытекает, вызывает искреннее недовольство. Да и в принципе это не только к вебу относится.
x-avatar[size=lg] {
width: 3.5rem;
height: 3.5rem;
}
А теперь представьте, что этот адаптивный компонент (зависимый от размера шрифта у пользователя за счёт использования rem
) надо поместить в неадаптивное окружение (не зависящее от размера шрифта). Чтобы не было такого, что рядом два компонента, и один компонент при увеличении шрифта увеличивается, а второй — нет. А всё спрятано в shadow DOM.
Чтобы решить эту проблему «компонентным» способом, по-хорошему при проектировании компонента вам нужно предусмотреть режим «адаптивный/пиксельный» (или явно, или в форме CSS-переменных). Но в девяти с половиной случаях из 10 это никому не нужно, и поэтому не делается. А в десятом вы приплыли.
И начинаются пляски с инъектированием CSS, либо через аппендинг <style>
(что в общем случае невозможно без знания жизненного цикла элемента), либо через new CSSStyleSheet
, а там свои подводные приколы. Главное, что только через JS. В то время как если писать неизолированный код, достаточно специально созданного для таких случаев нативного !important
.
this.innerHTML = "<h1>Santa's List</h1>…"
Настоящая проблема в том, что HTML не поддерживает полноценную шаблонизацию. И для шаблонизации начинают использовать Джаваскрипт. Один .js-файл можно подключить к двум разным проектам, и не придётся копипастить <h1>Santa's List</h1>
в код страницы и туда, и туда. Только вот Джаваскрипт не был предусмотрен для хранения структуры, и вы в придачу получаете кучу проблем. Например, внутри «Santa's List» можно спрятать зловредный код, и скрыть его от показа в редакторе при помощи вот таких техник. Если бы структура задавалась только в файле с разметкой, можно было бы проконтролировать отсутствие в неё скриптов (точки входа ведь известны — атрибуты обработчиков и скриптовый тег). Локализовать (делать локальные версии под разные языки) такой код, особенно если активно используется интерполяция — то ещё удовольствие. И так далее, и так далее.
Лично я для реюзабельности давно пришёл к внешней шаблонизации (раз уж внутреннюю не завезли). То есть, файлы с HTML-шаблоном <template>…</template>
идут в комплекте со скриптами и стилями, и подставляются при сборке конкретного проекта. А набор таких файлов при локализации проекта можно как одно целое отдать переводчику.
И это только два примера навскидку!
Я бы сказал, что компоненты в нынешнем виде — вообще плохо продуманная технология, минусы вы соберёте все, а плюсы, которые мог бы дать только компонентный подход, не поддерживаются. Пример такого «плюса»: чисто вспомогательные вещи типа отрисовки рамки выделения можно было бы делать на лету, из onpaint
, чтобы не создавать и не модифицировать левые узлы в DOM (во-первых, это ненужная нагрузка на браузер, особенно с учётом уровней перерисовки, во-вторых — это редкий пример, когда императивный код выразительнее, чем декларативный — представьте, какое удовольствие писать инверсную рамку выделения на HTML/CSS). Но этого-то как раз и нельзя.
файлы с HTML-шаблоном
<template>…</template>
P.S. Иногда полезно вместо <template>…</template>
оформлять шаблон как <div class="template">…</div>
(.template { display: none; }
), чтобы этот шаблон перед инстанцированием можно было гибко настраивать не парся строку (<template>…</template>
это просто строка, лишённая всякой семантики), а пользуясь встроенными в браузер средствами доступа (селекторами).
Ваше замечание важное, но ИМХО смешивания адаптивного и неадаптивного - это как раз то, чего желательно избегать
А теперь представьте, что этот адаптивный компонент (зависимый от размера шрифта у пользователя за счёт использования
rem
) надо поместить в неадаптивное окружение (не зависящее от размера шрифта)
ну просто rem не к тому элементу применили, надо же иметь ввиду содержимое компоненты, ее назначение и возможные применения. Зачем кому то аватар в rem ? он должен занимать блок который ему выделили, а своих размеров иметь не должен такой блок, макс следить за отношением сторон. Применяйте текстовую адаптацию к тексту и всё хорошо будет.
this.innerHTML
и так никогда пожалуйста не делайте, очень больно потом выковыривать.
Отличная статья.
Я проходил кучу бесплатных курсов по фронту, но там ни разу не затрагивалось создание и настройка своих компонентов без фреймворков.
Ещё проще с обработкой событий работать, не нужно отдельно циклом проходить по созданным элементам.
<script
src="hello-world.js"
></script>
Чтобы не было проблем с поиском некоторых элементов в доме, нужно для скрипта добавить type="module"
if (!this.shadowRoot) {
необязателен, но обеспечивает возможность генерации HTML на стороне сервера при помощи декларативного shadow DOM.
Обязателен, так как EDGE крашнет страницу с ошибкой если вызвать attachShadow для элемента у которого этот метод уже вызывался.
В целом хорошая статья, и я рад, что вы тоже популяризируете веб-компоненты – они очень крутые. Но тема раскрыта далеко не полностью.
Напоминаю, что весь этот код по-прежнему ванильный. В нём не используется ни один фреймворк, библиотека или инструмент сборки
Код - офигенный!
Но не надо передергивать. Это не просто ванильный код, это свой фреймворк с JS и кастомными компонентами. Использовать такое в коммерции - спорное решение. Долго (т.е. дорого) и его поддержка загнется, как только из компании уволятся евангелисты этого подхода.
Веб-разработка на ванильном HTML, CSS и JavaScript