1 of 45

Web Components

Шаблонизаторы и пользовательские тэги.

2 of 45

Web Components

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

  • Templates
  • Custom Elements
  • Shadow DOM
  • Slots

3 of 45

Templates

01. <template>

<img src="logo.png"/>

<div class="comment"></div>

04. </template>

02.

03.

4 of 45

Преимущества <template>

  1. Содержание остаётся эффективно инертным, пока оно не

активировано. По сути разметка является скрытым DOM'ом и не рендерится.

  1. Содержимое шаблона не порождает побочных эффектов: картинки не грузятся, видео не проигрывается, скрипты не выполняются.
  2. Так же внутренности шаблона не видны при выборе нод через

querySelector() или document.getElementById() .

  1. Шаблоны могут находиться в любом месте, даже там, где есть строгие ограничения на содержимое, например в <head> или <table> .

5 of 45

Пример <template>

  1. <template>
  2. <img src="kitten.jpg"/>
  3. </template>

6 of 45

Пример <template>

  1. <template>
  2. <img src="kitten.jpg"/>
  3. </template>

  • var content = document.querySelector('template').content;
  • document.querySelector('#container').appendChild(
  • content.cloneNode(true));

7 of 45

<template> и стили

  1. Стили тоже не применяются, пока шаблон не активирован
  2. Добавлен селектор :host(<selector>) , который позволяет применять селекторы только на шаблон
  3. НО при использовании стилей в шаблоне надо очень аккуратно смотреть на оптимизаторы кода, которые делают структурные изменения

8 of 45

Custom Elements

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

9 of 45

Yandex.Mail

10 of 45

Yandex.Mail

11 of 45

Yandex.Mail

12 of 45

Верстка яндекс почты без WebComponents

  1. <div class="header">...</div>
  2. <div class="folders">...</div>
  3. <div class="messages">...</div>
  4. <div class="message">...</div>

13 of 45

  1. <header>...</header>
  2. <folders>...</folders>
  3. <messages>...</messages>
  4. <message>...</message>

Верстка яндекс почты c WebComponents

14 of 45

Пример кастомного элемента

15 of 45

Неразрешённые (Unresolved) элементы

Так как регистрация кастомных элементов происходит путём вызова document.registerElement() в тэгах script, а построение DOM ещё до вызова script’ов, мы можем столкнуться с такой вещью как неразрешённые элементы.

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

Всплывает событие unresolved

16 of 45

Lifecycle callbacks

connectedCallback

экземпляр элемента добавлен в документ

disconnectedCallback

экземпляр элемента удалён из документа

adoptedCallback

экземпляр элемента был перемещён в новый документ

attributeChangedCallback(attrName, oldVal, newVal)

добавление/удаление/изменение аттрибута attrName

17 of 45

18 of 45

attributeChangedCallback

  • Обратите внимание, что нужно наблюдать за атрибутами, чтобы запустить колбэк attributeChangedCallback() когда они изменятся. Это делается через вызов геттера observedAttributes() в конструкторе, который содержит оператор return возвращающий массив с именами атрибутов, которые вы хотите наблюдать:

19 of 45

Теневой DOM («Shadow DOM»)

Используется для инкапсуляции. �Благодаря ему в компоненте есть собственное «теневое» DOM-дерево, к которому нельзя просто так обратиться из главного документа, у него могут быть изолированные CSS-правила и т.д.

20 of 45

21 of 45

22 of 45

.attachShadow()

Вызов elem.attachShadow({mode: …}) создаёт теневое дерево.

Есть два ограничения:

  • Для каждого элемента мы можем создать только один shadow root.
  • В качестве elem может быть использован пользовательский элемент (Custom Element), либо один из следующих элементов: «article», «aside», «blockquote», «body», «div», «footer», «h1…h6», «header», «main» «nav», «p», «section» или «span». Остальные, например, <img>, не могут содержать теневое дерево.

23 of 45

.attachShadow({ mode: … })

Свойство mode задаёт уровень инкапсуляции. �У него может быть только два значения:

  • "open" – корень теневого дерева будет доступен через elem.shadowRoot.

Любой код может получить теневое дерево элемента «elem»

  • "closed" – elem.shadowRoot всегда возвращает null.

До теневого DOM в таком случае мы сможем добраться только по ссылке, которую возвращает attachShadow.

Встроенные браузерные теневые деревья, такие как у �<input type="range">, по умолчанию закрыты.

Програмнно из JS-a до них не добраться.

24 of 45

25 of 45

26 of 45

Инкапсуляция теневого DOM

Теневой DOM отделён от главного документа:,

  • Элементы теневого DOM не видны из обычного DOM через querySelector. В частности, элементы теневого DOM могут иметь такие же идентификаторы, как у элементов в обычном DOM.�Они должны быть уникальными только внутри теневого дерева.

  • У теневого DOM свои стили. �Стили из внешнего DOM не применяется.

27 of 45

28 of 45

Слоты теневого DOM (композиция).

  • HTML-элемент <slot> является частью набора технологии Web Components, является заполнителем внутри веб компонента, который можно заполнить собственной разметкой, которая позволяет создавать отдельные деревья DOM и представлять их вместе.

29 of 45

Слоты теневого DOM (композиция).

Многим типам компонентов, таким как вкладки, меню, галереи изображений и другие, нужно какое-то содержимое для отображения.

Так же, как встроенный в браузер <select> ожидает получить контент пунктов <option>, компонент <custom-tabs> может ожидать, что будет передано фактическое содержимое вкладок, а <custom-menu> – пунктов меню.

Код, использующий меню <custom-menu>, может выглядеть так:

30 of 45

31 of 45

Именованные слоты

В теневом DOM <slot name="X"> определяет «точку вставки» – место, где отображаются элементы с slot="X".

Затем браузер выполняет «композицию»: берёт элементы из обычного DOM-дерева и отображает их в соответствующих слотах теневого DOM-дерева. В результате мы получаем именно то, что хотели – компонент, который можно наполнить данными.

После выполнения скрипта структура DOM выглядит следующим образом (без учёта композиции):

32 of 45

Именованные слоты

Чтобы отобразить содержимое, для каждого <slot name="..."> в теневом DOM браузер ищет slot="..." с таким же именем в обычном DOM. Эти элементы отображаются внутри слотов:

33 of 45

34 of 45

Развёрнутое DOM-дерево

…но развёрнутое DOM-дерево существует только для целей отображения и обработки событий. Это то, что мы видим на экране. Оно, в некотором плане, «виртуальное». Фактически в документе расположение узлов не меняется.

Это можно легко проверить, запустив querySelectorAll: �все узлы находятся на своих местах.

document.querySelectorAll('user-card span').length; // 2

35 of 45

36 of 45

Слот по умолчанию

Первый <slot> в теневом дереве без атрибута name является слотом по умолчанию. Он будет отображать данные со всех узлов светлого дерева, не добавленные в другие слоты

Например, давайте добавим слот по умолчанию в наш элемент <user-card>; он будет собирать всю информацию о пользователе, не занесённую в другие слоты

37 of 45

38 of 45

Обновление слотов

Что если внешний код хочет динамически реагировать на изменение данных в слотах?

Браузер наблюдает за слотами и обновляет отображение при добавлении и удалении элементов автоматически.

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

39 of 45

API слотов

Как мы видели раньше, JavaScript смотрит на «реальный», а не на развёрнутый DOM. Но если у теневого дерева стоит {mode: 'open'}, то мы можем выяснить, какие элементы находятся в слоте, и, наоборот, определить слот по элементу, который в нём находится:

40 of 45

Стилизация WebComponent’ов

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

41 of 45

42 of 45

Селекторы для веб компонентов

  • :host -> Выбирает ваш компонент.
  • :host(context-name) -> Выбирает ваш компонент по конкретному классу
  • :host-context(context-tag-name) -> Выбирает корень теневого DOM вашего компонент который был вложен в другой элемент указанный в context-tag-name селекторе.
  • ::slotted(element) -> Возвращает элемент по селектору если он был вложен в слот вашего компонента.

43 of 45

WebComponents

https://xrem.github.io/web/web-component-example.html

Что дальше?

Больше веб-компонентов без framework’ов:

https://lit.dev

Либо, отдельно шаблонизаторы (handlebars, ejs, etc…)

Либо, полноценные UI Framework’и: Vue, React, Angular.

44 of 45

Время вопросов

45 of 45

Спасибо за внимание