Как стать автором
Поиск
Написать публикацию
Обновить

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

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

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

Честно говоря, я не понимаю почему в 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 из статьи - просто лишний код

А может объяснить в чем прикол экспортировать функцию customElements.define потом дожидаться domload и только потом регистрировать компоненты?

Например для ленивой инициализации. Скажем, есть компонент, который изначально не нужен и инициируется только при каком-то пользовательском действии. Но вообще вкусовщина автора.

По воводу DOMContentLoaded, предполагаю, чтобы не грузить основной поток выполнением лишнего JS и работой с DOM, которая возникает при инициализации пользовательских элементов. То есть сначала даём браузеру спокойно пропарсить разметку, загрузить стили и другие ресурсы, а потом, когда он построил DOM, уже выполнять скрипты. Но тоже похоже на вкусовщину автора.

Для ленивой инициализации лучше динамический импорт файла с компонентом использовать.

Есть еще один способ передавать данные в компонент - через его дочерние элементы
```<custom-select>
<option value="v1"> item 1</option>
...
</custom-select>```

В момент инициализации своего компонента - парсите вложенные в него теги. В принципе так что угодно в рамках разумного, хоть json можно передать в <code style="display:none">{ myjson: { a:1 }}</code> если ситуация позволяет.

Тогда уж <script type="application/json">{ ... }</script> для JSON (или любого другого текстового формата), если смотреть с точки зрения стандарта.

Интересно, насколько всё это тормозит по сравнению с настоящим HTML/CSS?

Пользуюсь во всю, причем в реакте. Никаких тормозов, больше того, веб-компонент существенно производительнее чем тоже самое но на композиции из реакт элементов

Так вы не с тем сравниваете. Сравнивать надо с обычной вёрсткой. Без JS'а и реакта.

а на обычной верстке это не будет работать, с чем сравниваем ? тормоза будут там где вы добавили connectedCallback, вот насколько они будут тормозить. настолько и будут тормознее, магии нет, это по прежнему DOM классы и наследованы прототипно (а да ещё есть мааленький оверхед на +1 поиск по прототипу) больше тормозов там неоткуда взяться.

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

Пользовательские элементы построены на основе стандартного DOM API браузера. Вызовы методов жизненного цикла пользовательского элемента, по сути, биндинги к подкапотному API на C++. Браузер это и так делает при построении DOM, но разработчикам даётся возможность немного влезть в этот процесс и выполнить какой-то JS.

Поэтому технически пользовательскте элементы медленнее, чем простой HTML/CSS. И требуют загрузки, парсинга и выполнения 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> это просто строка, лишённая всякой семантики), а пользуясь встроенными в браузер средствами доступа (селекторами).

<template> — не просто строка, а экземпляр DocumentFragment. То есть кусок DOM, который браузер распарсил и собрал из строки. И с этим фрагментом DOM можно работать через DOM API и потом вставить в нужное место или многократно клонировать.

С <div style="display:none"> проблема в том, что если внутри есть ресурсы, <script>, <style>, <link> — они будет загружаться/выполняться, чего от шаблона обычно не хочется. Фишка <template> как раз в том, что внутри него всё инертно. Такое можно получить, разве что, от <script>, но в нём всё будет строкой.

<template> — не просто строка, а экземпляр DocumentFragment

Век живи, век учись! Спасибо, я как-то упустил.

Ваше замечание важное, но ИМХО смешивания адаптивного и неадаптивного - это как раз то, чего желательно избегать

Это же просто пример. Пример, кстати, свежий (плюс-минус год) и взят с Habr Q&A. Человек там интересовался, как одну из самых популярных каруселей заадаптивить. Я ему ответил: напиши рядом размер с !important, и всё. А если бы это был изолированный компонент?

Но есть и другие причины не изолировать код от внешней стилизации. Например, чтобы можно было после каждого обновления компонента не патчить его, если создаваемая им структура не поменялась.

А теперь представьте, что этот адаптивный компонент (зависимый от размера шрифта у пользователя за счёт использования rem) надо поместить в неадаптивное окружение (не зависящее от размера шрифта)

ну просто rem не к тому элементу применили, надо же иметь ввиду содержимое компоненты, ее назначение и возможные применения. Зачем кому то аватар в rem ? он должен занимать блок который ему выделили, а своих размеров иметь не должен такой блок, макс следить за отношением сторон. Применяйте текстовую адаптацию к тексту и всё хорошо будет.

this.innerHTML

и так никогда пожалуйста не делайте, очень больно потом выковыривать.

Зачем кому то аватар в rem ?

Если мы говорим про аватар как учебный пример, то таких вопросов быть не должно. А если про реальное применение, я бы начал с вопроса, зачем вообще аватар оформлять компонентом.

и так никогда пожалуйста не делайте, очень больно потом выковыривать

Почему это адресовано мне, а не автору статьи, код из которой я процитировал? Я себе такое позволяю только при написании шаблонизаторов, но не в прикладном коде.

Отличная статья.

Я проходил кучу бесплатных курсов по фронту, но там ни разу не затрагивалось создание и настройка своих компонентов без фреймворков.

Ещё проще с обработкой событий работать, не нужно отдельно циклом проходить по созданным элементам.

<script src="hello-world.js"></script>

Чтобы не было проблем с поиском некоторых элементов в доме, нужно для скрипта добавить type="module"

if (!this.shadowRoot) { необязателен, но обеспечивает возможность генерации HTML на стороне сервера при помощи декларативного shadow DOM.

Обязателен, так как EDGE крашнет страницу с ошибкой если вызвать attachShadow для элемента у которого этот метод уже вызывался.

В целом хорошая статья, и я рад, что вы тоже популяризируете веб-компоненты – они очень крутые. Но тема раскрыта далеко не полностью.

Напоминаю, что весь этот код по-прежнему ванильный. В нём не используется ни один фреймворк, библиотека или инструмент сборки

Код - офигенный!

Но не надо передергивать. Это не просто ванильный код, это свой фреймворк с JS и кастомными компонентами. Использовать такое в коммерции - спорное решение. Долго (т.е. дорого) и его поддержка загнется, как только из компании уволятся евангелисты этого подхода.

Показанный код — это встроенные методы работы с DOM, которые одинаковы для всех браузеров, документированы и описаны в стандартах. Автор не разработал ничего своего, чтобы назвать это фреймворком. Он лишь показал пример разработки средствами стандартного DOM API.

Фреймворки поверх этих API есть и обычно они прячут многое под капот, упрощая написание кода и снижая количество ручной работы и вызовов DOM. Они вполне используются в продакшине очень даже крупными компаниями.

С наскока не могу понять (ткните пожалуйста пальцем), получилось ли в итоге главная киллер-фича фреймворков - реактивная связь между переменной и шаблоном. Например если я создам переменную seconds и буду раз в секунду её автоматически увеличивать, то смогу ли я где-то в шаблоне вставить нечто вроде {{seconds}} чтобы вместо этой конструкции выводилось текущее значение переменной seconds при КАЖДОМ её изменении?

А откуда появилась мысль, что такая цель ставилась?

Эта "убийственная" фича убивает качество конечного продукта, порождает килотонну глюков UI, потому что для того, чтобы эти глюки отсутствовали нужно написать портянку кода, который будет устранять то, что при НЕреактивном программировании просто не возникло бы.

Очередной глюк "убийственного" "реактивного" программирования увидел недавно.
Заказывал пиццу в Домино Пицца. Так вот, там после заказа можно открыть "страницу" для отслеживания прогресса, на которой, кроме прочего, есть дата и время заказа вида "xx мая 2025 yy:zz". Так вот, если рефрешить эту страницу, то изначально мелькает время GMT (т.е -3 часа от МСК) и май в виде "May", а потом это изменяется на кириллицу и время МСК.
Это я спецально ничего не искал, это то, что тупо бросается в глаза.

Не видел ни одного сайта (не только в РУ сегменте), сделанного с помощью реактивных фреймворков, которые бы не глючил постоянно или хотя бы временами (за исключением примитивных туду). Кол-во визуальных глюков несравнимо с олдскульными HTML/CSS/jQuery сайтами даже примерно. Их в разы больше.

Ну и время на разрабтку в разы больше уходит, пока вы там со своим скрамом все утрясете на тему, как очередную кнопку раскрашивать, почему у фронтендеров при запросах ошибки CORS лезут (Hello противоестественное отделение фронтендеров от бекендеров), почему бекенд возвращает не тот json и прочие прелести глупостей, объединяемых понятием Separation of Concerns(хинт - фактически, ничего вы разделяете, т.к. нельзя отделить пчел от цветов и ожидать получения меда).

Ну что за цирк! Это абсолютно неподдерживаемый код: 4 простеньких компонента, а уже какая-то каша.

Статья явно рассчитана на здешнюю публику: бэкенд-разработчиков предпенсионного возраста, которые в последний раз фронт еще на таблицах верстали и сейчас жестко ненавидят текущие фронтенд фреймворки, потому что им слишком лень переучиваться; они уверены, что всё можно делать по старинке, хотя сложность задач на фронте возросла с тех пор в десятки, если не в сотни, раз.

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