Использование только базового API веб-компонентов

Веб-компоненты - это набор низкоуровневых характеристик браузера, которые позволяют нам писать инкапсулированные, модульные и повторно используемые элементы HTML.
Веб-компоненты работают в любой стандартной веб-среде, которая поддерживает базовый HTML и JavaScript, и мы можем повторно использовать их в приложениях. которые мы построили с использованием разных технологий.

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

  • HTML, CSS и Javascript.
  • Модули Javascript
  • Стрелочные функции

Основные особенности веб-компонентов:

  • Пользовательские элементы
  • Шаблоны
  • Теневой DOM

Мы собираемся создавать наш пользовательский компонент ‹my-component› шаг за шагом.

<my-component>
    <h1>Hello world!</h1>
</my-component>

В настоящее время ваш браузер не поддерживает тег ‹my-component›. Когда браузер находит неизвестный HTML-тег, например ‹my-component›, он отображает его как встроенный элемент и продолжает визуализацию следующих элементов. Используя API пользовательских элементов, мы можем сообщить браузеру, что делать с нашим новым тегом HTML.

Давайте добавим тег скрипта внизу нашего элемента ‹body›:

<!DOCTYPE html>
<html>
    <body>
        <h1>Hello world!</h1>
     <script>
      /*the code of our 
       component go here.
      */
     </script>
    </body>
</html>

Чтобы создать собственный элемент, мы собираемся создать класс, расширяющий класс HTMLElement. Этот класс является базовым для всех остальных собственных элементов HTML, таких как элемент ‹input›.

class MyComponent extends HTMLElement{
  connectedCallback() {
     console.log('my component is connected!');
  }
}

После создания нашего класса мы можем связать его с именем тега, определив его в реестре настраиваемых элементов.

customElements.define('my-component', MyComponent);

Когда браузер создает экземпляр нашего настраиваемого тега, он запускает некоторые обратные вызовы жизненного цикла. Для простоты единственными методами жизненного цикла, которые мы увидим, являются ‹connectedCallback ()› и ‹disconnectedCallback ()›, как мы можем видеть ниже:

class MyComponent extends HTMLElement{
    constructor() {
      super();
      /*called when the class is 
      instantiated
      */
    }
connectedCallback() {
     /*called when the element is 
      connected to the page.
      This can be called multiple 
      times during the element's
      lifecycle.
      for example when using drag&drop
      to move elements around
     */
   }
disconnectedCallback() {
   /*called when the element
     is disconnected from the page
   */
}

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

class MyComponent extends HTMLElement{
    connectedCallback() {
        this.style.color = 'red';
    }
}

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

class MyComponent extends HTMLElement{
  constructor() {
   super();
   this.addEventListener('click',      
   () => {
         this.style.color === 'red'
         ? this.style.color = 'blue':
          this.style.color = 'red';
   });
  }  
  connectedCallback() {
      this.style.color = 'blue';
  }
}

Результирующий код:

<!DOCTYPE html>
<html>
<body>
<my-component>
    <h1>Hello Rick!</h1>
</my-component>
<script>
 class MyComponent extends HTMLElement  
 {
    constructor() {
      super();
      this.addEventListener('click',
      () => {
         this.style.color === 'red'
         ? this.style.color = 'blue':
          this.style.color = 'red';
       });
    }
connectedCallback() {
      /*called when the element is 
        connected to the page
      */
      this.style.color = 'blue';
    }
 }
customElements.define('my-component', MyComponent);
</script>
</body>
</html>

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

Браузер предоставляет нам элемент ‹template›, чтобы сделать это эффективно. Этот элемент позволяет нам заранее задать структуру части HTML и при необходимости эффективно клонировать ее. Сделать это быстрее, чем каждый раз воссоздавать одну и ту же структуру HTML.

Давайте создадим ‹template› внутри тегов body:

<template>
    <h1>Hello Rick!</h1>
</template>

Как мы видим, «Привет, Рик!» больше не отображается на странице, потому что внутри нее ничего не отображается и не выполняется.

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

Результирующий код:

<!DOCTYPE html>
<html>
<body>
<template>
    <h1>Hello Rick!</h1>
</template>
<my-component></my-component>
<script>
 class MyComponent extends HTMLElement {
 
  constructor() {
    super();
    this.addEventListener('click',
    () => {
         this.style.color === 'red'
         ? this.style.color = 'blue':
          this.style.color = 'red';
    });
  }
connectedCallback() {
   /*called when the element is 
        connected to the page
   */
this.style.color = 'blue';
const template = 
    document.
      querySelector('template');
    
    const clone = 
    document.
      importNode(template.content,    
      true);
    
    this.appendChild(clone);
  }
 }
customElements.define('my-component', MyComponent);
</script>
</body>
</html>

Веб-компоненты дают нам возможность использовать «ShadowDOM», эта функция теперь встроена в браузер, поэтому, если мы добавим дочерние элементы в Shadow DOM компонента, они не будут дочерними элементами наш элемент. Тем не менее, вместо этого они будут заключены в теневой корень.

Этот теневой корень - это особый тип узла DOM, который инкапсулирует элементы внутри себя. Стили, скрипты и другие элементы, определенные внутри этого теневого корня, не просачиваются, и наоборот, поэтому мы добиваемся инкапсуляции.

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

Для использования теневого корня в нашем компоненте замените строку: this.appendChild (clone) на this.attachShadow ({mode: ‘open’}) и this.shadowRoot.appendChild (клон):

Окончательный результирующий код:

<!DOCTYPE html>
<html>
<body>
<template>
    <h1>Hello Rick!</h1>
</template>
<my-component></my-component>
<script>
class MyComponent extends HTMLElement {
constructor() {
    super();
    this.addEventListener('click',
    () => {
         this.style.color === 'red'
         ? this.style.color = 'blue':
          this.style.color = 'red';
    });
   }
connectedCallback() {
  /*called when the element is 
    connected to the page
  */
this.style.color = 'blue';
 
  const template = 
   document.querySelector('template');
    
  const clone =    
  document.
  importNode(template.content, true);
   
  //this.appendChild(clone);
this.attachShadow({ mode: 'open' });         
  this.shadowRoot.appendChild(clone); 
   }
 }
customElements.define('my-component', MyComponent);
</script>
</body>
</html>

Заключение

Здесь мы увидели, как создать базовый веб-компонент с использованием ванильного JavaScript. Вы можете писать веб-компоненты, используя только базовые API веб-компонентов. Использование базового API может быть хорошим выбором, если вы хотите, чтобы зависимости были низкими. В противном случае рекомендуется использовать облегченные библиотеки, чтобы повысить удобство разработки и уменьшить количество шаблонов.

Примечание от JavaScript In Plain English: Мы всегда заинтересованы в содействии продвижению качественного контента. Если у вас есть статья, которую вы хотите отправить в JavaScript In Plain English, отправьте нам электронное письмо по адресу [email protected] с вашим именем пользователя Medium, и мы добавим вас в качестве автора.