...

суббота, 29 мая 2021 г.

[Перевод] Использование веб-компонентов при работе над GitHub

Мы, сотрудники GitHub, гордимся тем, что наша платформа обеспечивает тем, кто ей пользуется, первоклассный «опыт разработчика» (Developer Experience, DX). Значительная часть наших усилий сосредоточена на фронтенде системы, который мы стремимся сделать настолько простым, быстрым и доступным, насколько это возможно. Для проекта таких масштабов, как GitHub, это — та ещё задача. В кодовой базе нашего фронтенда, как и во многих других подобных кодовых базах, применяются компоненты — независимые, изолированные фрагменты кода, пригодные для многократного использования. Они позволяют командам, которые занимаются проектом, создавать тщательно проработанные интерфейсы, делая своё дело быстро и эффективно, но при этом не отступая от высоких стандартов качества, принятых в GitHub.

Мы, в работе над GitHub, широко используем веб-компоненты. У нас имеется почти два десятка опенсорсных веб-компонентов, и ещё несколько десятков компонентов, код которых закрыт.

Как мы к этому пришли


Когда, больше десяти лет тому назад, платформа GitHub только появилась, наш фронтенд был представлен сравнительно небольшой кодовой базой, в которой, в основном, использовалась библиотека jQuery. После того, как прошло десять лет и было написано примерно 85000 строк кода, наш фронтенд дорос до таких размеров, когда начинают проявляться болезни роста. Мы, в итоге, ушли от jQuery (по причинам, о которых мы, в своё время, подробно рассказывали) и начали использовать новые технологии, которые могли бы помочь нам лучше решить стоящие перед нами задачи.

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

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

Два наших первых пользовательских элемента были выпущены в 2014 году. Это были <relative-time> и <local-time>, которые умели показывать время и дату в удобной форме, а так же — <include-fragment>, который позволял нам выполнять отложенную загрузку HTML-фрагментов. Постепенно к нам пришло понимание того, какими мощными возможностями могут обладать подобные элементы. Тогда мы приступили к широкомасштабной замене существующих механизмов кодовой базы на новые. Например — поменяли наше модальное диалоговое окно facebox на веб-компонент <details-dialog>. Теперь у нас были самые разные компоненты — от весьма универсальных, многоцелевых сущностей, реализующих распространённые шаблоны поведения, вроде <remote-input>, до узкоспециализированных компонентов, таких, как элемент <markdown-toolbar> и родственные ему элементы.

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

Улучшение процесса создания компонентов


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

▍Фреймворк ViewComponent


Мы переориентируем наш Rails-код на использование ViewComponent — фреймворка для создания в Rails компонентов, пригодных для многократного использования. То, что создаётся с помощью ViewComponent, тесно связано с веб-компонентами. Дело в том, что между компонентами, разрабатываемыми в рамках этого фреймворка, и веб-компонентами может существовать прямая взаимосвязь. Это позволяет нашим программистам работать над одной и той же абстракцией, относящейся и к фронтенду, и к бэкенду.

▍Библиотека Catalyst


Catalyst — наша опенсорсная библиотека, применение которой упрощает разработку веб-компонентов, стала той самой движущей силой, которая объединяет некоторые из наших «лучших практик». Catalyst продвигает применение TypeScript-декораторов, что позволяет отказаться от больших объёмов шаблонного кода, который нужно писать в ходе разработки веб-компонентов.

Разработчики библиотеки Catalyst вдохновлялись замечательной библиотекой Stimulus и базовым классом LitElement от Google. Она спроектирована в расчёте на решение особого набора задач, стоящих перед нашими разработчиками. Наши внутренние DX-исследования показали, что применение Catalyst даёт, в сравнении со старыми подходами, существенные улучшения в деле написания кода компонентов.

Здесь можно почитать подробности о Catalyst — о соглашениях, принятых в этой библиотеке, о паттернах и антипаттернах её использования.

▍Вспомогательные инструменты


Мы предлагаем разработчикам набор опенсорсных конфигураций линтера. Общие правила, применяемые при написании кода, выражены в плагине eslint-plugin-github. А для более глубокой проверки кода веб-компонентов мы используем плагин eslint-plugin-custom-elements. Оформление этих плагинов в виде опенсорсных проектов позволило нам убрать код из монолита, но при этом оставаться последовательными при работе над проектом.

У нас, кроме того, имеются внутренние тесты, которые направлены на проверку того, следуют ли программисты рекомендованным подходам к разработке, и того, что они больше не используют паттерны и модели, реализующие некие схемы поведения элементов системы, признанные устаревшими. Один из наших тестов направлен на контроль того, чтобы разработчики не применяли бы в новом коде паттерн facebox, признанный устаревшим. Этот тест предлагает использовать в качестве альтернативы facebox элемент <details-dialog>.

class FaceboxDeprecationTest < Test::Fast::TestCase
  EXPECTED_NUMBER_OF_FACEBOXES = 44

  # Find facebox triggers set with rel=facebox, in either HTML attributes or
  # as part of hash assignment (for rails helpers)
  REGEX_FOR_FACEBOX_BINDING = %r|rel\s*[=:]>?\s*["']?facebox|
  REGEX_FOR_DATA_FACEBOX = %r|data-facebox\s*=>?\s*|

  def test_limit_facebox
    actual_rel_facebox = grep(REGEX_FOR_FACEBOX_BINDING, options: %w[-In], paths: %w[app/**/*.erb])
    actual_data_facebox = grep(REGEX_FOR_DATA_FACEBOX, options: %w[-In], paths: %w[app/**/*.erb])
    count = actual_rel_facebox.count("\n") + actual_data_facebox.count("\n")

    assert_operator count, :<=, EXPECTED_NUMBER_OF_FACEBOXES, <<-EOL
It looks like you added a facebox. Please use <details-dialog> instead.

If you must increment EXPECTED_NUMBER_OF_FACEBOXES in this test, please
/cc @github/ui-frameworks-reviewers in your pull request, as we may be able to help! Thanks.
EOL

    assert_equal EXPECTED_NUMBER_OF_FACEBOXES, count, <<-EOL
It looks like you removed a facebox. YOU ARE AWESOME!
Please decrement EXPECTED_NUMBER_OF_FACEBOXES in this test and treat yourself to
something special.  You deserve it.
EOL
  end
end

Новый жизненный цикл наших веб-компонентов


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

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

▍Catalyst и начало жизненного цикла компонента


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

Для регистрации веб-компонента может понадобиться некоторый объём шаблонного кода, но мы упрощаем эту задачу за счёт применения особого соглашения об именовании сущностей и благодаря наличию некоторого количества TypeScript-декораторов. То, что в Catalyst называется Actions, позволяет решить задачу организации прослушивания событий проще, чем она решается через поддержку глобальных прослушивателей событий. Внесение изменений в то, что в Catalyst называется Targets, в применении к существующему HTML-коду, лучше, чем рендеринг HTML-шаблонов в браузере.

▍Извлечение компонента из монолита


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

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

▍Реализация особых требований, предъявляемых к опенсорсным компонентам


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

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

Отличным примером компонента, прошедшего все стадии «опенсорсного» жизненного цикла, является элемент <typing-effect>. Одна из команд GitHub недавно создала прототип элемента интерфейса, напоминающего терминал, текст в котором появлялся так, будто кто-то набирает этот текст на клавиатуре. Ранее мы пользовались весьма качественной и надёжной библиотекой typed.js, с помощью которой добивались анимированного эффекта ввода текста с клавиатуры. Изначально команда решила снова обратиться к этой библиотеке.

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

Тогда была создана первая версия нашей собственной анимации ввода текста, предназначенная для нового интерфейса. Благодаря использованию Catalyst на это ушло меньше дня, а объём кода не превышал 40 строк. Поняв, что использование этого эффекта может заинтересовать и другие команды, мы решили вывести соответствующий элемент в опенсорс. Мы отрефакторили код компонента, избавив его от всех зависимостей, «извлекли» его из библиотеки Catalyst и сделали опенсорсным, назвав <typing-effect>.

Результаты


В целом — мы в восторге от тех изменений, которые мы внесли во фронтенд GitHub с момента предыдущей публикации на схожую тему. В соответствии с проведённым нами внутренним опросом разработчиков, оказалось, что им приятно работать с Catalyst и ViewComponent!

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

Что дальше?


Мы продолжаем выводить в опенсорс всё больше универсальных веб-компонентов, реализующих различные модели поведения. Всё это называется «GitHub Elements». Коллекцию этих элементов можно найти в этом репозитории, она согласована с нашей страницей на webcomponents.org.

Мы с интересом смотрим в будущее веб-компонентов и наблюдаем за предложениями по изменениям спецификаций HTML. Нам сейчас особенно интересны два таких предложения — Template Parts и Declarative Shadow DOM. Принятие этих предложений ещё сильнее облегчит процесс создания веб-компонентов и решит некоторые распространённые проблемы, касающиеся веб-компонентов, с которыми нам приходится бороться. Мы создали понифилл, реализующий минимальный работоспособный функционал предложения Template Parts. Он уже используем его в продакшне, и мы искренне заинтересованы в том, чтобы он нашёл бы применение в более широких кругах веб-разработчиков.

Пользуетесь ли вы веб-компонентами в своих проектах?


Adblock test (Why?)

Комментариев нет:

Отправить комментарий