Быстрый старт с WebComponents

Веб-компоненты это набор стандартов определяющих программные интерфейсы для организации компонентной архитектуры. Все они реализованы в современных версиях браузеров, т.е. не требуют подключения библиотек или транспиляторов кода, однако, если нужна совместимость например с Internet Explorer 11, то и библиотеки и транспиляторы использовать видимо все-таки придется.

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

Все эксперименты приводимые далее проверялись в Chrome и Firefox может быть даже не самых новых версий.
Итак начнем.

Для начала создадим директорию для проекта и перейдем в него.

mkdir mywebcomp
cd mywebcomp


Выполним:

npm init 


в этом каталоге ответив на все вопросы по умолчанию.

Создадим в каталоге файл index.html с самым простым содержимым.



















Добавим тег для элемента, имя должно обязательно содержать дефис, это сигнал для подсистемы CusomElements для попытки определения этого элемента как надстраивающего стандартные.










Добавим класс обработчик в теге script.



В модульном теге script, мы определили новый класс который c помощью метода customElements.define () определили за тегом my-webcomp. А добавив код в метод connectedCallback () мы обеспечили его вызов при добавлении реализации компонента в дерево. Результат уже можно посмотреть в браузере:

h8peqvzesjscex546s9he2xndqc.png

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














В коде компонента вставку строки c html мы заменили на получение элемента шаблона по id. Импорт, т.е. создание копии этого элемента и прицепление к содержимому текущего.

id назван в нотации camelCase т.к. все айдишники элементов прокидываются в глобальное пространство имен и при использовании дефисов или других спец. символов доступ к ним может быть менее элегантен. Т.е. мы могли бы вместо:

           let tplEl = document.querySelector('#myWebCompTemplate');
           let html = document.importNode(tplEl.content, true);


написать в одну строчку:

let html = document.importNode(myWebCompTemplate.content, true);


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






При таком раскладе алерт будет показываться сразу при загрузке страницы.

Теперь повесим обработчик клика мышки для нашего компонента который будет выводить алерт сообщение.





Теперь при нажатии на сообщение у нас будет реакция на действия пользователя.

psvcpclb5o5nlzbhfqxvweozoda.png

Аргументом метода showMessage () также объявляется объект event, который хранит данные о событии, например координаты клика или ссылку на сам элемент.

Часто каждый конкретный элемент надо сконфигурировать уникальным для него образом, это можно сделать используя атрибуты.

Добавим второй экземпляр элемента и определим для каждого из них разные свойства greet-name значения которых будут выводиться при нажатии на элемент.





Теперь при нажатии на первый будет выводиться «This is the message for John», а на второй «This is the message for Josh».

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








Получается вот так:

xsgrity3vcy4h9olcpuppvcupl0.png

Вместо .textContent может быть .innerHTML или можно вызвать у объекта из селектора тот же метод .insertAdjacentHTML () мы делали в самом начале.

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

           this.attachShadow({mode: 'open'});


Теперь правда все DOM обращения к this придется заменить на this.shadowRoot благо их пока не так много.



Визуально работа этого кода опять никак не изменится, но теперь в глобальном пространстве имен не будет никакого helloLabel, а у на странице уже 2 элемента с таким идентификатором. А получить доступ к ним можно будет например вот так:

myWebComp.shadowRoot.getElementById('helloLabel');


и то если вы не закроете дерево передав соответствующий атрибут в методе .attachShadow ().

У нас получилось довольно много кода и размещать его прямо в html файле тоже не очень правильно. Поэтому создадим файл my-webcomp.js и перенесем в него наш класс предварив инструкцией export, а в теге script добавим импорт этого класса, чтобы получилось вот такое:



На работоспособности это никак не скажется, но теперь вся бизнес логика у нас отдельно в .js, конфигурация осуществляется в html атрибутами, а модульную асинхронную инициализацию берут на себя механизмы модульной и компонентной системы браузера.

Правда с этого момента открывать index.html как локальный для разработки не получится, т.к. браузер заблокирует загрузку скрипта с файловой системы. Если у вас есть nodejs можно поставить простейший веб-сервер:

npm i http-server -g


и запускать его командой http-server в каталоге с проектом, при запуске он подскажет хост и порт с которого можно открывать страницу

http://127.0.0.1:8080 


Которая и будет отныне адресом отладочной страницы с нашим элементом.

Немаловажным для разработки является написание тестов, они помогают удерживать код работоспособным в ходе развития его компонентов. Для тестирования веб-компонентов можно использовать mocha в режиме запуска из браузера.

Добавим пакеты.

npm i mocha chai wct-mocha --save-dev


Для этого создадим каталог test и добавим в него файл all.html такого содержимого:




   
   
   
   







Скопируем наш index.html в test/ задав ему имя my-webcomp.tests.html и добавим такое же содержимое head как и в all.html.

Однако, нам обычную инициализацию и манипуляции теперь надо будет заменить на производимую в рамках запуска тестов:



Теперь при заходе на

http://127.0.0.1:8080/test/all.html


будет показан отчет о выполнении тестов.

_7ubokve7hcudxp_djz5yecpkf8.png

Но может потребоваться запускать тесты автоматизировано и в разных браузеров для этого надо поставить специальную утилиту:

sudo npm install --global web-component-tester --unsafe-perm


и запускать вот так:

wct --npm


Эту строчку можно добавить в секцию test файла package.json и запускать как:

npm test


la_erpwvp142hir0gnvg21fgchg.png

Рекомендуется тестировать каждый значимый метод класса в рамках отдельного теста. Если надо сделать общую для каждого инициализацию ее следует определить в методе suiteSetup ():

suiteSetup(() => {

});


внутри suite.

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

npm publish 


или хотя бы

git push


Книг на тему пока не так уж много, но вся расширенная документация легко находится по словам Web Components, CustomElements, ShadowDOM, Native Template Tag, Custom Events.

Сверить свой код можно с репозиторием: https://bitbucket.org/techminded/mywebcomp

© Habrahabr.ru