mrssea

Лучшие практики для создания веб-компонентов

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

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

Некоторые общие хорошие практики, основанные на личном опыте:

Один веб-компонент, одна ответственность 🧐

Лучший подход при разработке веб-компонентов — максимально упростить его. Попробуйте создать простые компоненты с одной функциональностью. Не пытайтесь решить все проблемы одновременно. Если у вас есть сложные требования, разделите их на несколько более мелких компонентов,а в конце объедините их в один основной компонент со всеми необходимыми функциями. Помните, что если ваш компонент может обрабатывать только одну вещь, ему нужно очень мало атрибутов/свойств и событий, и его можно повторно использовать более одного раза.

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

Пространство имен и его глобальная область видимости

Custom Elements API позволяет расширять HTML и определять пользовательские теги для веб-компонентов. Проблема здесь в том, что вам нужно быть очень осторожным с именем тега, потому что тег будет общим для всего HTML-документа и не может быть определен снова:

 

class MyAwesomeComponent extends HTMLElement {...}
window.customElements.define('my-awesome-component', MyAwesomeComponent);
class MyAwesomeComponentV2 extends HTMLElement {...}
// Это вызовет ошибку
window.customElements.define('my-awesome-component', MyAwesomeComponentV2);

 

Error: Failed to execute ‘define’ on ‘CustomElementRegistry’: the name “my-awesome-component” has already been used with this registry
Error: Failed to execute ‘define’ on ‘CustomElementRegistry’: the name “my-awesome-component” has already been used with this registry

Это означает, что у вас не может быть двух пользовательских элементов с одним и тем же тегом.

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

Это также может произойти, если импортировать два файла, определяющих один и тот же компонент:

header.js

pageA.js
import './header.js';
Class PageA extends HTMLElement {
// Мы используем компонент header где-то внутри этого компонента PageA.
}
window.customElements.define('my-page-a', PageA);

 

pageB.js 

import './header.js';
Class PageB extends HTMLElement {
// Мы используем компонент header где-то внутри этого компонента PageB. 
}
window.customElements.define('my-page-b', PageA);import './header.js';
Class PageB extends HTMLElement {
// Мы используем компонент header где-то внутри этого компонента PageB. 
}
window.customElements.define('my-page-b', PageA);

app.js 

import './pageA.js';
import './pageB.js';  // this will cause the same error
Class App extends HTMLElement {
// Мы используем pageA и pageB в этом компоненте
}
window.customElements.define('my-app', App);

Так как решить эту проблему?

Было выдвинуто предложение о создании реестров пользовательских элементов, но пока оно готовится, вы используете Lit Element для использования 💪 элементов с ограниченной областью действия.

Если вы не используете LitElement, будьте осторожны с тегом, который вы выбираете для своего компонента:

Атрибуты/свойства для входящих данных, пользовательские события для исходящих данных

Разница между свойствами и атрибутами может сбивать с толку. Атрибуты предоставляются в самом HTML, а свойства доступны на узле DOM. Кроме того, некоторые свойства отражают свои значения как атрибуты, поэтому, если свойство изменяется с помощью JavaScript, соответствующий атрибут также изменяется одновременно, чтобы отразить новое значение.

Рассмотрим этот пример:

 

<!-- index.html -->
<img src="ingLogo.svg" class="orange" alt="ING logo" width="150" height="250" />
<!-- class, alt, width и height - Атрибуты --><!-- index.html -->

 

// index.js
const ingLogo = document.querySelector('.orange');
// className -это свойство
ingLogo.className = 'blackAndWhite'; // Атрибут class изменился с 'orange' на 'blackAndWhite'

Атрибуты

Используйте атрибуты для получения несложных данных в вашем компоненте. Попробуйте создать их для логических значений, которые не требуют пояснений, таких как ‘disabled’, ‘read-only’, ‘special-behavior-mode’… используйте их как флаги для передачи вашим компонентам, чтобы они вели себя так или иначе.

Не переопределяйте заданные автором или глобальные атрибуты, существующие во всех элементах HTML, например tabindex или role. Помните, что эта часть принадлежит конечному разработчику, который будет использовать ваш компонент.

Не применяйте css-классы на компонент самостоятельно, если вы хотите сообщить о происходящих изменениях в вашем компоненте, сделайте это, используя атрибуты, например disabled, но не добавляя класс. Другой способ сделать это — отправить событие.

 

<my-awesome-component disabled></my-awesome-component>

Свойства

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

 

const myConfig = {
    name: 'My name',
    surName: 'My name',
    address: 'My name',
    phone: 'My name',
       ...
}

Примечание. В этом примере используется синтаксис lit-html 

 

<my-awesome-component disabled .config="${myConfig}"></my-awesome-component>

 

Пользовательские события

Вызывайте пользовательские события, когда вашему компоненту нужно что-то передать вне этого компонента. Просто будьте осторожны с именем и тем, как вы используете свойства bubbles и composed:

 

class MyComponent extends HTMLElement {
  connectedCallback() {
    const event = new CustomEvent('my-component-hello-event', {
      detail: { message: 'Hello from MyComponent class' },
      bubbles: true,
      composed: true,
    });
    this.dispatchEvent(event);
  }
}

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

Если свойство composed имеет значение true, ваше событие будет всплывать сквозь границу теневого DOM в стандартную DOM, поэтому еще раз подумайте, нужно ли вам это или нет. Использование этого свойства не рекомендуется, когда оно не требуется, так как оно нарушает инкапсуляцию.

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

Не вызывайте события в ответ на настройку хостом свойства (нисходящий поток данных). Например, когда родительский компонент устанавливает новое значение свойства для дочернего компонента, дочернему компоненту не нужно информировать родительский компонент об этом изменении, поскольку родительский компонент уже знает об этом.

Выдавать ошибку при не выполнении ключевых требований

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

Первый из них — ничего не делать, молча терпеть неудачу, как это делают элементы DOM. Например, вы когда-нибудь замечали ошибку в своем браузере, когда делали что-то подобное?

 

<h1>
  <li>Link 1</li>
  <li>Link 2</li>
  <li>Link 3</li>
  <li>Link 4</li>
</h1>

Или когда вы делаете что-то вроде этого:

<input type="randomTypeIHaveJustInvented" value="123" />

Для этого последнего примера будет лучше создать исключение и помочь разработчику, использующему ваш компонент. Например:

 

<my-custom-input type="randomTypeIHaveJustInvented" value="123"></my-custom-input>

Error: Type 'randomTypeIHaveJustInvented' is not allowed. Please use these types: text, number, or password.

Вы должны проверить ключевые требования в вашем компоненте и выдать ошибку, если эти требования не выполнены. Однако старайтесь избегать console.log или console.warning по мелочам. Ваш компонент работает или не работает. Если не сработает, то сгенерировать исключение.

Почему? Просто потому, что вы никогда не узнаете, кто и где использует ваш компонент. Это очень сложно понять, когда вы создаете приложение, полное веб-компонентов. Вы пытаетесь решить требования этого приложения… Но что произойдет, если в будущем какой-то компонент будет использоваться другим приложением?

Создайте хороший README для вашего компонента 🤓

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

Продолжается обсуждение стандартного способа сделать это, который называется «custom-elements-manifest».

Кроме того, есть некоторые инструменты, такие как «web-component-analyzer», которые могут вам здесь помочь. Этот инструмент может автоматически генерировать файл readme для вашего компонента.

Пример

Описание в несколько слов.
# Установка
Фрагмент для копирования и вставки вместе со скриптом установки.
# Использование
Пример того, как любое веб-приложение может вызвать ваш компонент с некоторыми параметрами
# Атрибуты
| Name | Type | Description | Optional |
# Свойства
| Name | Type | Description | Optional |
# События
| Name | Type | Description |
# Примечания
Добавьте дополнительные примечания

Сделайте его адаптивным, доступным и настраиваемым

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

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

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

Кроме того, рекомендуется использовать shadowDOM для инкапсуляции стилей вашего компонента, чтобы ваши стили CSS не влияли на внешние стили; только с помощью пользовательских свойств CSS или шрифтов.

Установите стиль display для :host (например, block, inline-block, flex), если вы не предпочитаете inline по умолчанию. Попробуйте использовать SVG для изображений, встроенных в ваш компонент, не используйте здесь изображения base64, это сделает ваш компонент очень большим, и эти изображения не будут адаптироваться к изменениям размера экрана.

Подумайте о производительности 🚀

Веб-компоненты работают быстро. Им не требуется импорт какой-либо библиотеки, поскольку они используют стандартные веб-API для работы в браузере. Тем не менее, я рекомендую вам обратить пристальное внимание на время первой отрисовки.

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

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

Сделайте его простым в сопровождении 🛠

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

Для этого можно использовать следующие приемы:

Заключение

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

Оригинал статьи на английском https://medium.com/ing-blog/best-practices-to-build-web-components-6d517923fba4