Работа с моим первым веб-компонентом

Работа с моим первым веб-компонентом

23 ноября 2022 г.

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

Что именно?

На высоком уровне веб-компонент позволяет определить пользовательский элемент HTML. Так, например, я мог бы сделать это:

<h1>My Cats</h1>

<pet-cat name="Luna" age="11">
<pet-cat name="Elise" age="12">
<pet-cat name="Pig" age="9">

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

<h1>My Cats</h1>

<div>
    <h2>Luna</h2>
    <p>
    This cat is 11 years old.
    </p>
</div>

<div>
    <h2>Elise</h2>
    <p>
    This cat is 12 years old.
    </p>
</div>

<div>
    <h2>Pig</h2>
    <p>
    This cat is 9 years old.
    </p>
</div>

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

Я настоятельно рекомендую прочитать справочник MDN по веб-компонентам, так как он содержит очень подробные сведения, но главное строительные блоки состоят из:

* Возможность определить пользовательский элемент (pet-cat выше) в JavaScript * Shadow DOM, звучит очень круто, но в основном это способ сказать дерево документа, которое инкапсулировано внутри себя и отдельно от остального документа. Я видел отличный пример этого и не могу вспомнить источник, но подумайте о теге <video> и о том, как он имеет встроенные элементы управления для работы с видео. Это DOM, инкапсулированный внутри себя. * И, наконец, HTML-шаблоны, которые не рендерятся, а используются веб-компонентом для верстки. На самом деле я не касался этого аспекта в созданной мной демонстрации, так что это не обязательно на 100 %.

Веб-компоненты бывают двух основных видов:

  • Совершенно уникальные, как пример, который я привел выше.
  • Компоненты, которые изменяют существующие теги, распознаваемые с помощью синтаксиса as: <ul is="something-else">. Здесь мы столкнулись с проблемой с этим конкретным браузером. Safari не поддерживает этот стиль и, насколько мне известно, никогда не будет. Кто знает. Честно говоря, я нахожу этот стиль менее привлекательным, чем предыдущий, поэтому он меня не слишком беспокоит.

Хорошо, но почему?

Сразу я вижу, что веб-компоненты были бы большим подспорьем для библиотек пользовательского интерфейса. Я проверил, и хотя Bootstrap его не поддерживает, он у них на радаре. После использования BootstrapVue я могу сказать вам, что опыт использования Bootstrap с компонентами значительно лучше, чем "обычный" Начальная загрузка. В качестве примера, вот простой пользовательский интерфейс с вкладками:

<ul class="nav nav-tabs">
  <li class="nav-item">
    <a class="nav-link active" aria-current="page" href="#">First</a>
  </li>
  <li class="nav-item">
    <a class="nav-link" href="#">Second</a>
  </li>
  <li class="nav-item">
    <a class="nav-link disabled">Disabled</a>
  </li>
</ul>
<!-- tab content down here... -->

Хотя это не сложно, сравните это с этим:

<b-tabs content-class="mt-3">
    <b-tab title="First" active><p>I'm the first tab</p></b-tab>
    <b-tab title="Second"><p>I'm the second tab</p></b-tab>
    <b-tab title="Disabled" disabled><p>I'm a disabled tab!</p></b-tab>
</b-tabs>

Я также вижу, что это действительно полезно внутри организации, где на большом сайте необходимо создавать согласованные элементы UI/UX/и т. д. Использование веб-компонентов, безусловно, упростит эту задачу.

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

Как насчет примера?

Отдай мне котенка...

Для моего первого теста я создал быструю оболочку для PlaceKitten. Я создал файл cat.js и определил его следующим образом:

class PlaceCat extends HTMLElement {

    constructor() {

        super();

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

        const wrapper = document.createElement('div');

        let width = 500;
        let height = 500;

        if(this.hasAttribute('width')) width = this.getAttribute('width');
        if(this.hasAttribute('height')) height = this.getAttribute('height');

        const img = document.createElement('img');
        img.setAttribute('src', `https://placekitten.com/${width}/${height}`);
        wrapper.appendChild(img);

        shadow.appendChild(wrapper);

    }

}

customElements.define('place-cat', PlaceCat);

У меня есть класс, расширяющий базовый HTMLElement. У него должен быть конструктор, вызывающий super. Эта переменная shadow определяет «открытый» интерфейс, что означает, что родитель может «достучаться» до DOM, если это необходимо.

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

Мой DOM представляет собой тег div с изображением внутри. Когда я закончу его строить, я добавлю его к своей тени. Готово.

В конце обратите внимание на вызов define. Веб-компоненты должны быть в стиле kabab, т. е. что-то тире что-то.

В HTML-шаблон я просто включаю его и использую:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title></title>
</head>
<style>
<body>


<place-cat></place-cat>
<place-cat width="200" height="200"></place-cat>


<script src="cat.js"></script>
</body>
</html>

Вот результат:

Наверняка веб-боги предназначали компоненты для кошек, верно? Если вы откроете devtools, вы увидите их так же, как и любой другой элемент:

Если вы хотите, вы можете просмотреть его онлайн здесь: https://cfjedimaster.github.io/webdemos/webcomponents/test1. .html

Итак, этот первоначальный пример был настолько тривиальным, что он не работал бы очень хорошо в производственной среде. В частности, это потерпит неудачу в одном отношении. Если бы я использовал JavaScript, чтобы создать новый экземпляр элемента, а затем установить размеры, это не удалось бы:

let cat = document.createElement('place-cat');
cat.setAttribute('width', 200);
cat.setAttribute('height', 400);

document.querySelector('body').appendChild(cat);

Почему? Потому что веб-компонент должен определить, какие атрибуты он будет «прислушиваться» к изменениям, и должен иметь какую-то пользовательскую логику для реализации этих изменений. К счастью, это можно сделать двумя способами. Во-первых, мы определяем атрибуты, которые хотим отслеживать:

static get observedAttributes() { return ['width','height']; }

Затем мы можем использовать attributeCHangedCallback для обработки этих изменений. Выглядит это так:

attributeChangedCallback(name, oldValue, newValue) {
    // name is the attribute changing
    // old and new value represent the previous and new settings
}

Я обновил свой элемент кошки, чтобы использовать это:

class PlaceCat extends HTMLElement {

    getURL() {
        return `https://placekitten.com/${this.width}/${this.height}`
    }

    constructor() {

        super();

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

        const wrapper = document.createElement('div');

        this.width = 500;
        this.height = 500;

        if(this.hasAttribute('width')) this.width = this.getAttribute('width');
        if(this.hasAttribute('height')) this.height = this.getAttribute('height');

        const img = document.createElement('img');
        img.setAttribute('src', this.getURL());
        wrapper.appendChild(img);

        shadow.appendChild(wrapper);

    }

    static get observedAttributes() { return ['width','height']; }

    attributeChangedCallback(name, oldValue, newValue) {
        this[name] = newValue;
        this.shadowRoot.querySelector('img').src = this.getURL();
    }

}

// Define the new element
customElements.define('place-cat', PlaceCat);

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title></title>
</head>
<body>

<button id="makeCat">Make Cat</button>

<script src="cat2.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
    document.querySelector('#makeCat').addEventListener('click', () => {

        let cat = document.createElement('place-cat');
        cat.setAttribute('width', 200);
        cat.setAttribute('height', 400);

        document.querySelector('body').appendChild(cat);

    }, false);
}, false);
</script>
</body>
</html>

Обратите внимание, что я переключился на cat2.js в теге скрипта, чтобы сохранить свой начальный и «расширенный» компонент cat. Все, что я здесь сделал, это добавил обработчик щелчка к кнопке, а затем добавил элемент place-cat в тело. Если вы хотите попробовать это сами, загляните сюда: https://cfjedimaster.github.io/webdemos/webcomponents /test1a.html

Я не планировал создавать CodePen и не ожидал, что он не сработает, но все равно пошел дальше:

https://codepen.io/cfjedimaster/pen/GRQmRxG?embedable=true

Будет больше

Я только начал смотреть на это, но я определенно хочу копать больше. Существует несколько проектов, направленных на упрощение работы с веб-компонентами (я планирую рассмотреть Lit и Stencil), но, как всегда, я хотел бы услышать от людей, использующих это в дикой природе. Дайте мне знать, внедрили ли вы их в свою работу и что вы об этом думаете.

:::информация Также опубликовано здесь.

:::


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE