Веб-компоненты это набор стандартов определяющих программные интерфейсы для организации компонентной архитектуры. Все они реализованы в современных версиях браузеров, т.е. не требуют подключения библиотек или транспиляторов кода, однако, если нужна совместимость например с Internet Explorer 11, то и библиотеки и транспиляторы использовать видимо все-таки придется.
Данная статья ориентирована на начальный уровень подготовки и разработчиков имеющих опыт с тем или иным фронтенд фреймворком, но возможно, благодаря некоторым фокусам, будет интересна и многоопытным специалистам.
Все эксперименты приводимые далее проверялись в Chrome и Firefox может быть даже не самых новых версий.
Итак начнем.
Для начала создадим директорию для проекта и перейдем в него.
Выполним:
В этом каталоге ответив на все вопросы по умолчанию.
Создадим в каталоге файл index.html с самым простым содержимым.
Добавим тег для элемента, имя должно обязательно содержать дефис, это сигнал для подсистемы CusomElements для попытки определения этого элемента как надстраивающего стандартные.
Добавим класс обработчик в теге script
.
В модульном теге script
, мы определили новый класс который c помощью метода customElements.define()
определили за тегом my-webcomp
. А добавив код в метод connectedCallback()
мы обеспечили его вызов при добавлении реализации компонента в дерево. Результат уже можно посмотреть в браузере:
Однако, размещать html верстку прямо в код если и удобно для небольших кусочков, то вообще не правильно, что особенно сказывается когда кусочки разрастаются до приличных размеров. Например на форме с 20ю элементами, которую побить на подкомпоненты тоже не всегда может быть удобно. Поэтому мы для начала вынесем верстку в шаблон, который будет располагаться в том же html, хотя ничто не помешает его нам загрузить из отдельного файла при необходимости.
В коде компонента вставку строки c html мы заменили на получение элемента шаблона по id. Импорт, т.е. создание копии этого элемента и присоединение к содержимому текущего.
id назван в нотации camelCase т.к. все айдишники элементов прокидываются в глобальное пространство имен и при использовании дефисов или других спец. символов доступ к ним может быть менее элегантен. Т.е. мы могли бы вместо:
написать в одну строчку:
И этот код работал бы точно так же, но это считается не очень безопасным. Также если мы присвоим id нашему элементу, то сможем обращаться к нему из любой точки контекста как к экземпляру из глобального пространства имен вызывая методы и получая значения свойств.
Например вот так:
При таком раскладе алерт будет показываться сразу при загрузке страницы.
Теперь повесим обработчик клика мышки для нашего компонента который будет выводить алерт сообщение.
Теперь при нажатии на сообщение у нас будет реакция на действия пользователя.
Аргументом метода showMessage()
также объявляется объект event
, который хранит данные о событии, например координаты клика или ссылку на сам элемент.
Часто каждый конкретный элемент надо конфигурировать уникальным для него образом, это можно сделать используя атрибуты.
Добавим второй экземпляр элемента и определим для каждого из них разные свойства greet-name
значения которых будут выводиться при нажатии на элемент.
Теперь при нажатии на первый будет выводиться “This is the message for John”, а на второй “This is the message for Josh”.
Может так случиться что атрибут надо будет использовать не в обработке события, а прямо рендерить в шаблон, для этого мы добавим id целевому элементу и подставим значение из API сразу после рендеринга копии объекта шаблона.
Получается вот так:
Вместо .textContent
может быть .innerHTML
или можно вызвать у объекта из селектора тот же метод .insertAdjacentHTML()
мы делали в самом начале.
Долгое время использование id считалось дурным тоном, потому что на значительных объемах кода они могли дублироваться, что приводило к коллизиям. Однако, с появлением технологии теневого дерева можно внутреннее содержимое элемента изолировать использовать id, стили и прочие ресурсы без опасений. Для веб-компонентов включается теневое дерево следующим образом:
Теперь правда все DOM обращения к this
придется заменить на this.shadowRoot
благо их пока не так много.
Визуально работа этого кода опять никак не изменится, но теперь в глобальном пространстве имен не будет никакого helloLabel
, а у на странице уже 2 элемента с таким идентификатором. А получить доступ к ним можно будет например вот так:
И то если вы не закроете дерево передав соответствующий атрибут в методе .attachShadow()
.
У нас получилось довольно много кода и размещать его прямо в html файле тоже не очень правильно. Поэтому создадим файл my-webcomp.js
и перенесем в него наш класс предварив инструкцией export, а в теге script добавим импорт этого класса, чтобы получилось вот такое:
На работоспособности это никак не скажется, но теперь вся бизнес логика у нас отдельно в .js
, конфигурация осуществляется в html атрибутами, а модульную асинхронную инициализацию берут на себя механизмы модульной и компонентной системы браузера.
Правда с этого момента открывать index.html
как локальный для разработки не получится, т.к. браузер заблокирует загрузку скрипта с файловой системы. Если у вас есть nodejs можно поставить простейший веб-сервер:
И запускать его командой http-server в каталоге с проектом, при запуске он подскажет хост и порт с которого можно открывать страницу http://127.0.0.1:8080
Которая и будет отныне адресом отладочной страницы с нашим элементом.
На этом наверное хватит для первого раза, мы получили некоторый минимальный проект которому решай он конкретную задачу не стыдно было бы сделать npm publish
.
Книг на тему пока не так уж много, но вся расширенная документация легко находится по словам Web Components, CustomElements, ShadowDOM, Native Template Tag, Custom Events.