Как стать автором
Обновить

Комментарии 27

Вэб компоненты сложны и многословны. Классы более сложные и менее гибкие абстракции чем функции (как например в Реакт). Создавать и обновлять ДОМ вручную тоже не легкое занятие если это не один единственный элемент. Но Реакт слишком много пытается на себя взять для библиотеки: стейт, асинхронность, исключения, компиляция, сервер... Предлягаю посмотреть легкую библиотеку где из Реакта взято только создание и обновление ДОМ. Ее можно использовать как отдельно, так и внутри Вэб Компонентов для управлени ДОМ.

Без сервера тяжело монетизировать, а с сервером есть облака с подпиской.

Честно говоря, я не понимаю почему в html просто не сделали, чтобы каждый документ мог использовать произвольные теги, которые потом определять через css. И для совместимости иметь стандартный сет, чей css был бы предопределен браузером. Ведь, без JS, то что описано в статье работать не будет...

div

О! Век живи, век учись!

без JS, то что описано в статье работать не будет...

И не должно. Веб-компоненты они как-бы для SPA/PWA.
В них же нет ничего нового, то же самое можно воспроизвести на div-ах. Разница в том, что если вы сделаете такой-же компонент аватара на дивах, и будете отдавать его в html с сервера, то если в html будет список из 1000 аватаров... ну вы поняли.
С веб компонентами у нас есть функция для создания такого аватара. Сколько раз ее вызвать уже ваше дело.

А при чем тут это? В html гитхаба есть веб-компоненты, но предварительно грузятся скрипты которые их регистрируют.

А может объяснить в чем прикол экспортировать функцию 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 поиск по прототипу) больше тормозов там неоткуда взяться.

А я думаю, что там целый новый уровень абстракции навёрнут. Плюс, там, где должен быть нативный рендеринг компонента и его содержимого, это всё делается в интерпретаторе JS.

Просто поделюсь своими мыслями. В свое время перегорел к веб-разработке из-за необходимости поддержки работы 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 и кастомными компонентами. Использовать такое в коммерции - спорное решение. Долго (т.е. дорого) и его поддержка загнется, как только из компании уволятся евангелисты этого подхода.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий