24 июня 2020

Владимир Лежнин

Ведущий веб-программист

От библиотеки компонентов к дизайн-системе

Разработка UI-компонентов — одна из самых затратных задач при разработке фронтенда. Чтобы оптимизировать процессы, некоторые компании поручают её выделенной команде. Мы же решили пойти другим путём: сделать библиотеку, чтобы компоненты туда могли добавлять разработчики из разных команд. В этой статье расскажу, как наша библиотека организована.

Задачи библиотеки компонентов

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

Интерфейсы продуктов для управления инфраструктурой и сайтами от ISPsystem

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

  1. Поддержка версионности
  2. Простое добавление компонентов
  3. Совмещение кода компонента с визуальными требованиями к нему.

Как мы эти требования реализовали, подробно расскажу дальше. Если коротко, то использовали Verdaccio, Stencil, импорт svg-файлов из «Фигмы».

«А давайте сделаем не одну, а две библиотеки»

Несколько лет назад мы начали писать новый интерфейс для всей линейки продуктов ISPsystem. Переделку начали с биллинга, а когда встал вопрос о старте ещё одного проекта, задумались о переиспользовании написанного кода. Тогда и пришла мысль о библиотеке компонентов.

Один из наших разработчиков был покорен идеей веб-компонентов, в ту пору не так известных, и заразил нас. Идея и правда отличная: веб-компоненты можно использовать в любых веб-приложениях и на любых фреймворках. А Angular мы только начинали использовать и не были уверены (или душу грела мысль о возможности выбора), что новый проект будем делать именно на нем. Поэтому мы решили сделать две библиотеки: одну на веб-компонентах, вторую на Angular. Первую назвали ispui, вторую ngispui.

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

Светлая идея разбилась о реальность. Писать на чистых веб-компонентах не так удобно и быстро, как на Angular, поэтому мы их не разрабатывали. К тому же поддерживать три проекта (основной, плюс две библиотеки) было не очень удобно. Добавили в ispui пару веб-компонентов и несколько CSS-компонентов, и оставили. Сосредоточились на компонентах на ngispui.

Так основная работа стала вестись в библиотеке для Angular. За время существования мы добавили туда более 50 компонентов. И это действительно принесло плоды: новые проекты стартовали меньше, чем за год, тогда как первые мы делали почти два года.

Библиотека компонентов на Angular: как работает

Первая версия библиотеки публиковалась в Git. О её устройстве мы рассказывали еще год назад. Схема рабочая, но не идеальная: все версии были прибиты железно, поэтому воспользоваться преимуществами версионности было нельзя. Как следствие, возникали проблемы при сборке Angular, если у пакетов из библиотеки был один и тот же зависимый пакет, но разных версий.

В комментариях под той статьей нам посоветовали локальный npm-регистр пакетов verdaccio. Мы развернули его локально, с авторизацией через GitLab. Жить стало проще: можно пользоваться всеми фишками версионности. И скрипт публикации был переписан.

При публикации в verdaccio мы смотрим на бранч, из которого происходит публикация, если это не master, то в момент публикации к версии добавляем суффикс «dev-текущая дата и время» (например 1.0.0-dev12.12.2019). В package.json в репозитории версия остается без префикса. Поднимаем версию и пишем changelog руками. А когда бранч с новой версией компонента мёржится в master, в CI запускается публикация стабильной версии. И ничего в этот момент не нужно править в исходниках. Что удобно.

Требования к добавлению компонентов в библиотеку просты, это тесты + демо-страница. Для быстрого добавления компонентов в библиотеку у нас есть скрипт на plop, который генерирует шаблон демо-страницы и добавляет его в демо-приложение. Разработчику остаётся только сделать пример UI-компонента с описанием API.

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

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

Каждому компоненту — своя версия

Библиотека сделана как одно приложение с демо, но каждый компонент публикуется отдельно. То есть каждый компонент — это отдельный пакет со своими версиями.

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

Помимо плюсов, такой подход несёт и минусы. Получается, что при публикации одного компонента надо:

  • собирать всю библиотеку, проходить все тесты;
  • если есть зависимые пакеты, добавлять их в демо-приложение;
  • прописывать зависимости компонента в package.json.

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

Собираем демо-версию компонента для быстрого ревью

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

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

Превращаем библиотеку в дизайн-систему

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

Дизайнеры сделали в «Фигме» страницы с описанием компонентов. Мы договорились об едином наименовании. И теперь при сборке библиотеки мы просто скачиваем страницы из «Фигмы» и вставляем их в виде SVG-файлов в демо страницы с компонентами. Решение не идеальное: на SVG-картинке текст не выделишь и поиском по странице не найдёшь, но прочитать можно.

Когда в библиотеке кроме кода появились дизайнерские требования и правила, она стала полноценной дизайн-системой. Теперь ей пользуются и разработчики, и дизайнеры. Выглядит так:

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

Считаем, как часто используется компонент

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

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

Когда к нам в очередной раз пришли практиканты, мы попросили их написать сервис сбора статистики по использованию компонентов из библиотеки. Ребята сделали сервис из нескольких частей:

  1. Первый скрипт забирает информацию о зависимостях из package.json;
  2. Второй скрипт парсит html на использование UI-компонентов и атрибутов этих компонентов;
  3. Компонент-виджет отображает собранную статистику.
Чем больше пользователей у компонента, тем страшнее разработчику с ним хулиганить

Обновляем библиотеку на новую версию Angular

Библиотека компонентов на Angular получилось хорошей: много компонентов, рабочая инфраструктура, понятная схема работы. Но есть у нее и больные места. Одно из них — обновление на актуальную версию Angular.

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

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

Возрождаем библиотеку веб-компонентов

Когда с базовой функциональностью новых продуктов закончили, пришло время добавлять возможности, для которых Angular было уже недостаточно. Встала новая задача — плагины на фронтенде. Появилась идея делать их на веб-компонентах и встраивать в приложение Angular. Так получится не привязываться к версии Angular и не понадобится пересобирать плагины при обновлении приложения. Идея отличная, только вот для разработки плагинов требуются UI-компоненты.

Так мы вернулись к библиотеке веб-компонентов — ispui. Чтобы не писать на чистых веб-компонентах, мы стали искать библиотеки и фреймворки, которые могли упростить нашу задачу. Angular Elements отпал сразу, из-за привязки к версии Angular, и сама реализация пока сыровата для использования в качестве библиотеки. LitElement довольно простой, но на тот момент не был совместим с Typescript, а Typescript мы любим. Наши ребята в свободное время написали свой класс для написания веб-компонентов AbstractElement, но ресурсов на его разработку и поддержку у нас нет, а на сдачу делать не вариант. А вот Stencil нас покорил: поддержка Typescript, TSX, ангуляр-подобный синтаксис с декораторами, готовая экосистема с тестами, генерацией документации и лоадер для использования в Angular и в других фреймворках.

Сейчас активно разрабатываем библиотеку ispui, учитывая предыдущий опыт и пожелания. Что уже сделали:

  1. Монорепозиторий управляемый lerna. С ним можно независимо запускать сборку, тесты и публикацию отдельных пакетов.
  2. Для каждого компонента своя демка в виде html-файла. При сборке она добавляется к демо приложению.
  3. Для stencil-компонентов генерируется документация, которая вставляется в демо-приложение с описанием API компонента, используемых CSS-переменных.
  4. Для автоматического поднятия версий и генерации чейнджлога используем написание коммитов по конвенции коммитов. Для этого удобно использовать утилиту git-cz.

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

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

  1. Используйте готовые инструменты по управлению инфраструктурой. Например, lerna.
  2. Используйте автоматический листинг, настроенные tslint/eslint, stylelint, commitlint. Они позволяют валидировать проект автоматически.
  3. Автоматизируйте рутинные процессы. Например, создание демо-страниц.
  4. Используйте фреймворки и библиотеки с развитой инфраструктурой с настроенной тестовой средой, и автоматической генерации документации.

Владимир Лежнин

Ведущий веб-программист