
Работа с моим первым веб-компонентом
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), но, как всегда, я хотел бы услышать от людей, использующих это в дикой природе. Дайте мне знать, внедрили ли вы их в свою работу и что вы об этом думаете.
:::информация Также опубликовано здесь.
:::
Оригинал