Web Components. Назад в будущее.

Kliment Ru
Angular Soviet
Published in
5 min readJan 8, 2019

Наверное, каждый frontend разработчик задавался вопросом, какую библиотеку или фреймворк выбрать как основу для своих приложений Angular, React или Vue, но, по сути, у всех современных технологий есть общий трэнд. Мы разбиваем приложение на небольшие компоненты и используем их, строя устойчивую архитектуру. Безусловно, компонентный подход — это одна из страниц в истории frontend, которая перевела разработку на новый уровень. К сожалению, мало говорится о том, что можно использовать компонентный подход на чистом JS, CSS и HTML. Я имею в виду технологию Web Components, появившуюся еще в далеком 2011 году. Давайте разберем на практике из чего состоит Web Components и насколько технология пригодна для использования.

Поддержка браузерами

Самой большой проблемой Web Components было отсутствие поддержки большинством браузеров, и, если посмотреть статистику, стандарт v0 практически нигде не поддерживался. Сейчас ситуация изменилась. Вот актуальная информация на начало 2019 года:

Выглядит действительно круто, демо приложение запустилось в chrome и safari вообще без компиляции.

Microsoft как всегда немного портит общую картину, но есть полифилы.

Из чего состоят Web Components

Начать следует с того, что Web Components объединяет в себе несколько спецификаций. На данный момент это:

  • Custom Elements v1;
  • HTML templates;
  • Shadow DOM v1;
  • ES modules.

Разберем их по порядку.

Custom Elements v1

Позволяет добавить на страницу собственный элемент, содержащий в себе несколько DOM nodes. Вот как это выглядит:

создание компонента
использование компонента
Результат

Как видно из кода выше, первым делом нужно создать класс и наследовать его от HTMLElement, затем с помощью customElemens задать ему имя тэга. Важно, что имя должно состоять из 2х частей, разделенных дефисом. Компонент готов к использованию.

HTML templates

С помощью элемента template можно создать шаблон вместе со стилями, который будет позже вставлен в разметку страницы или, в данном случае, в отдельный компонент. Элемент slot указывает, куда будет вставлена разметка, полученная из родителя между открывающим и закрывающим тэгами компонента. Также существует возможность создавать несколько именованных слотов. Вот как это будет выглядеть:

Shadow DOM v1

Shadow DOM позволяет создать изолированное от основного документа DOM дерево со своими инкапсулированными стилями:

Разберем расположенный выше код. Внутри класса TodoButton ключевое слово this указывает непосредственно на класс, т.е. на данный компонент. Метод attachShadow добавляет в компонент корневой элемент DOM дерева, который после становится доступен через this.shadowRoot, и к нему прикрепляется созданный выше шаблон. Вот как будет выглядеть <todo-button>+</todo-button> в chrome devTools:

Создание и распространение событий

Работу с событиями рассмотрим на примере удаления задачи из списка задач.

Так выглядит DOM дерево:

В нем есть компонент задачи (todo-item), который принимает на вход id, name и completed. Вот как он выглядит изнутри:

todoItem.js

В компоненте из шаблона через querySelector происходит получение элемента кнопки удаления, затем через addEventListener добавляется подписка на событие клика по кнопке. После клика компонент распространяет новое событие deleteTodo. Параметр { bubbles: true } определяет, что данное событие должно всплывать. Теперь можно в родительском компоненте отловить событие deleteTodo:

Благодаря описанному выше свойству bubbles, событие можно отловить на уровне списка и сделать одну подписку, а не добавлять её на каждый item. Через ev.target получаем элемент, который вызвал событие удаления, после чего можно отфильтровать список задач и удалить элемент через removeChild.

В отличии от основных фреймворков, в которых обработка событий описывается в шаблоне, в Web Components обработка происходит через прослушивание события с помощью addEventListener. С одной стороны, это немного ухудшает читаемость кода; с другой стороны, дает возможность повесить обработчик на родительский элемент, а не на каждый item списка, что теоретически должно увеличить производительность, особенно при работе с большими массивами данных.

Изменение атрибутов компонента

К уже рассмотренному выше приложению в компонент todo-item добавим свойство completed(признак завершенности задачи), которое будет меняться по клику на галку около элемента.

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

В дочернем компоненте отследить изменения можно с помощью хука attributeChangedCallback(attrName, oldVal, newVal), который возвращает три аргумента: название атрибута, старое значение и новое значение. Но по умолчанию изменение атрибутов не наблюдается, и если необходимо отслеживание, то надо добавить атрибут в геттер static get observedAttributes(). Код компонента после изменения:

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

Выводы

Технология Web Components поддерживается практически полностью, её будет легко изучить и применить разработчикам, которые использовали хотя бы одну библиотеку с компонентным подходом.

В маленьких и средних проектах смело можно её использовать для кастомизации элементов интерфейса и обходиться без внешних зависимостей.

В больших проектах, конечно, не обойтись без фреймвокров, т.к. они помимо компонентного подхода дают еще окружение, приятные фичи и большое комьюнити, но Web Components можно применить и в них для избавления от legacy кода и миграций (допустим с AngularJs на Angular).

Ссылки

--

--