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

СПА против МПА

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

Веб-компоненты

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

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

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

Веб-компонент SPA

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

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

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

Освещенное государственное управление

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

Переменная cards имеет декоратор @property, что означает, что рендеринг этого компонента будет запускаться всякий раз, когда эта переменная изменяется либо внутри компонента, либо снаружи компонента. через метод доступа к свойству, который будет иметь то же имя. Вы могли заметить, что этот веб-компонент, в свою очередь, использует веб-компонент игровой карты, поэтому для запуска обновления отдельно выбранной карты метод доступа к свойствам запускается следующим образом:

(this.renderRoot.querySelector(`#game-card-${card.id}`) as GameCard).selected = card.selected

Это позволит избежать повторного рендеринга всей руки, а только повторно рендерить целевую карту.

Службы и контроллеры

Однако, поскольку в сентябре 2021 года была выпущена вторая версия Lit, я столкнулся с совершенно другой проблемой. Не было доступных версий маршрутизаторов Lit 2.0, i18n или сервисов управления формами, которые я мог бы имитировать в проекте. Мне пришлось писать свои собственные базовые сервисы, черпая вдохновение везде, где только можно было найти.

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

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

Вот пример самого простого контроллера

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

Привязать этот контроллер к хост-компоненту очень просто:

private user = new UserController(this);

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

Создание и обслуживание

Я адаптировал конфигурацию Vite.js для работы с lit.dev, так как внутри она использует свертывание. Мне пришлось добавить несколько плагинов для работы с eslint и Typescript, но в этом не было ничего необычного. Dockerfile упаковывает образ с помощью nginx, настроенного для работы с brotli для современных представлений. Приложение размещается самостоятельно с использованием бесплатного уровня Cloudflare для DNS.

Что дальше?

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