Компонентная разработка - самый популярный подход к созданию веб-приложений в настоящее время. Истоки такой архитектуры появились много лет назад из таких фреймворков, как Angular, React и т. Д. Позже к компонентным подходам присоединились многие другие фреймворки, и даже сегодня у нас есть новая волна - веб-компоненты.

Почему компоненты становятся такими популярными?

Я не хочу углубляться в преимущества декларативного программирования по сравнению с императивным, но в целом я могу выделить такие причины:

  1. Язык разметки HTML довольно прост для понимания и повторного использования в любом веб-приложении без особых настроек.
  2. Разработчик в основном ориентирован на верстку бизнес-страниц с использованием собственных и настраиваемых html-элементов.
  3. Безопасный DOM - каждый компонент имеет свою собственную изолированную область HTML (ShadowDOM в случае нативных веб-компонентов), которая не загрязняет таблицы стилей или элементы глобальной среды.
  4. Подходы к управлению состоянием позволяют создавать компоненты как отражение данных. Это дает веб-разработчикам их давнюю мечту: разделить код между представлением DOM и бизнес-логикой.

Я фанат новой технологии веб-компонентов, которая позволяет использовать все эти функции изначально. Это совершает некоторую революцию в мире веб-разработки, потому что теперь нет границ между различными фреймворками и / или библиотеками. Нативные веб-компоненты можно использовать везде в браузере (полифиллы?), Потому что нативный слой наверняка одинаков для всех веб-приложений. Красиво, не правда ли?

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

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

Пример использования 1. Реализация нового элемента пользовательского интерфейса.

Представьте, что наш клиент понял, что ему не нравятся наши элементы пользовательского интерфейса с выбором чисел и он хочет чего-то нового. Он решил представить новый элемент пользовательского интерфейса Slider. Несомненно, Slider должен быть новым элементом пользовательского интерфейса, поскольку нет такого нативного элемента, который мы могли бы использовать. Таким образом, этот элемент пользовательского интерфейса должен обеспечивать возможность выбора некоторого числового значения, перемещая указатель по строке. Ползунок должен генерировать событие «change» каждый раз, когда пользователь изменяет его значение и имеет возможность вернуть его внутреннее значение. Итак, интерфейс слайдера может выглядеть так:

  • выдать «изменение»
  • getValue (): Число
  • setValue (значение: Число): void

Итак, мы представили новый элемент пользовательского интерфейса, который имеет собственный макет, поведение и API:

Пример использования 2. Предложение новой подсказки

Хорошо, мы решили задачу Slider и нужно переходить на другой. Представим, что нашему клиенту не нравятся наши всплывающие подсказки на всех страницах (мы используем нативные с помощью атрибута title) и он хочет иметь 3 отдельных всплывающих подсказки:

  • всплывающая подсказка с предупреждением - должна быть выделена желтой рамкой и иметь значок предупреждения внутри
  • всплывающая подсказка об ошибке - должна быть выделена красной рамкой и иметь значок ошибки внутри
  • всплывающая подсказка - должна быть выделена зеленой рамкой

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

Придумаем что-нибудь. Так что API вроде этого может работать:

Разве такой API не нарушает читабельность нашей DOM? Разве не становится сложнее поддерживать? Что, если в будущем наш клиент попросит другие аналогичные функции, которые можно будет применить к любому элементу?

Пример использования 3. Расширение API нативных элементов

Представьте, что нашему клиенту не нравятся обычные текстовые поля для выбора даты, нативные (input type = ’datetime’) тоже не то, что ему нужно для пользовательского интерфейса. После некоторых решений он представил новый календарь, который может появляться для наших входов при нажатии.

Что делать в этом случае?

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

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

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

Что такое Matcher?

Что, если появится возможность расширить набор собственных глобальных атрибутов функций нашими собственными? Это может уберечь нас от ненужных компонентов-контейнеров и упростит наш HTML.

Вот какие мысли привели меня к созданию моей новой библиотеки MatcherJS: https://github.com/telenko/matcher-js

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

Matcher имеет очень похожий API на customElements, поэтому он не вызовет сложной настройки или даже (возможно) позволит нам иметь один общий API для определения расширений DOM в будущем.

Матчеры подключают / отключают поток

Итак, алгоритм выглядит так:

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

Примечание. MatchersController создает отдельный экземпляр Matcher для каждого элемента. Это означает, что сопоставитель не загрязняет прототип элементов html, он просто «подключается» к нему, но все же является отдельным объектом.

Хорошо, давайте посмотрим, что у него есть.

Добавление простого сопоставителя

MatcherJS имеет только несколько экспортируемых функций. Одна из них - функция defineMatcher. Давайте попробуем использовать его для определения простого сопоставителя:

Хорошо, можем ли мы использовать это сейчас в нашей DOM? Что-то еще нужно для настройки сопоставителей?

Нет, с таким html сопоставление будет применяться к обоим элементам:

Именованный сопоставитель

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

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

Пытаемся переписать все наши решения с помощью сопоставителя

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

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

Итак, что использовать? Зачем тогда нужны сопоставители?

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

Матчеры можно использовать, если

1. Вы собираетесь создать фичу css + js:

Сложная анимация, которая может потребовать некоторых вычислений js, некоторых эффектов, которые не поддерживаются css и т. Д.

2. Вы собираетесь представить новую функцию, которую можно добавить к любому элементу:

Всплывающая подсказка, перетаскиваемые элементы (знаю о нативном коде, но он может поддерживать лучшую анимацию), поддержка изменения размера элемента и т. Д.

3. Вы собираетесь (безопасно) расширить API любого нативного элемента:

Добавление функции изменения размера в ‹th› или выбор даты в ‹input›

4. Ненужный контейнер:

Может быть полезно избавиться от компонентов-контейнеров.

Не следует использовать сопоставители, если

1. Новая функция будет добавлять прямые дочерние элементы к элементу или управлять им с помощью ShadowDOM.

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

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

3. Компоненты, ответственные за данные (компоненты-контейнеры)

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

Заключение

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