Как разработать браузерное расширение в Chrome на React: разбираем на примере Cloudhood

Всем привет! Меня зовут Егор, я технический лидер в команде, которая занимается разработкой личного кабинета Cloud.ru. Некоторое время назад загорелись идеей сделать браузерное расширение на React, но не было хороших гайдов (либо я их не нашел), поэтому в итоге написали свой) В статье поделился нашим опытом и наработками, а также рассказал, зачем мы вообще взялись за этот open source проект.

9cfd6466ec14ef800981feef4b362776.png

Чем полезны браузерные расширения? В статье не буду разбирать их «минусы» и «плюсы». Скажу только, что расширения позволяют делать полезные инструменты как для повседневного использования, так и для решения рабочих задач. Вот несколько примеров:  

  • блокировка всплывающей рекламы,  

  • видоизменение html-страниц,  

  • подмена тела и заголовков запросов и ответов,  

  • создание технических тулзов для библиотек в разработке.

И многое-многое другое) Все ограничено только вашей фантазией и API браузера. Мы, например, решили сделать расширение, которое помогает при разработке приложений, когда много команд параллельно пилит свои фичи. 

Зачем мы этим занялись 

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

Кратко расскажу про наш CI/CD:

  1. Сначала разработчик делает фичу.

  2. Затем пушит код в GitLab.

  3. В GitLab запускаются пайплайны, прогоняются различные проверки кода, затем собирается приложение и в Docker деплоится на dev-стенд.

Кажется, все готово. Остается только понять, как разным командам одновременно иметь свою версию приложения на dev-стенде для тестирования фич? Тут и приходит на выручку наше расширение Cloudhood. Теперь с помощью прокидывания хедеров в запросы мы можем подтягивать конкретную версию проекта (как у нас всё реализовано можно почитать в статье).

Конфигурация расширения

Чтобы сконфигурировать приложение, сначала сделаем стандартную структуру проекта.

Пример структуры проекта

Пример структуры проекта

Для этого:

  • Создаем package.json.

  • Делаем README.md с подробным описанием.

  • Добавляем конфиги линтера, претира и тайпскрипта.

  • Настраиваем husky и lint-staged для прекоммит-хуков, в которых будем прогонять линтер и претир.

  • Добавляем .gitignore — чтобы лишнего в репозиторий не летело.

  • Выбираем лицензию — нам лучше всего подошла Apache License 2.0.

  • Подбираем удобную структуру проекта. Мы использовали Feature-Sliced Design:

ba130e825431ca0e71006dedba32ce42.png

Теперь добавим файл manifest.json — он понадобится для публикации расширения:

ecd050fc22b424e3a45d40271365ee85.png

Вот из чего он состоит:

  • manifest_version — версия файла. Лучше использовать v3, поскольку Chrome постепенно уходит от v2.

  • icon и default_icon — чтобы Chrome понимал, какая иконка будет отображаться в браузере у всплывающего окна расширения:

8c3b36f75e9c41bfd7272bdec1e4f69e.png

  • default_popup — html или попап, который будет отрисовываться при клике на иконку расширения (т. е. по сути это визуальная часть нашего расширения).

  • icons — чтобы расширение отображалось на разных страницах интернет-магазина Chrome. Каждая страница имеет свои размеры, например:

    e4c02c3b2c1c6f3b5f93662e897cbfc2.png
  • host_permissions — помогает определить, на каких сайтах будет работать расширение (наше работает на любом).

  • permissions — описываем те АPI Chrome, которые планируем использовать. Важный момент: если забыть какой-то из пермишенов, то при установке расширения он не сработает. Почему так? Во-первых, для безопасности пользователей (потенциально расширения могут воровать данные пользователей и делать другие нехорошие вещи). Во-вторых, чтобы Chrome проще было проверять, для чего вы планируете их использовать (нужно написать обоснование для каждого). 

  • background — для работы сервис-воркера (как именно он работает рассмотрим позже).

Вот и всё, переходим к написанию компонентов.

API Chrome: пример использования

Итак, у нас есть сверстанное приложение. Посмотрим, как использовать API Chrome для взаимодействия с сайтом. 

Мы в расширении используем его для видоизменения запросов. Вот как это работает:

  • Используем storage.local для сохранения хедеров для запросов при их удалении, изменении и добавлении. По сути, это аналог localStorage браузера:

bca7b6cba668de799308cd7b454ee531.png

  • Затем сообщаем cервис-воркеру, о том, что нужно обновить хедеры с помощью chrome.runtime.sendMessage:

714b215a3d9968a6cbcef86b2499adc9.png3ccbc58494f753d49fb52b48296154e6.png

Если сообщение пришло, он достает из стораджа сохраненные значения хедеров и затем применяет новые хедеры ко всем запросам браузера с помощью API chrome.declarativeNetRequest.updateDynamicRules:

9390580708d5f8bd6901febef46b3a4a.png

Для чего нужен background.ts разобрались, переходим к запуску и сборке.

Запуск и сборка

Для запуска и настройки приложения React-проект можно запустить локально либо собрать приложение и загрузить в браузер. Первый способ удобнее: так лучше работает hot-reload и проще дебажить. 

Поэтому добавим скрипт start в package.json, как показано на скрине. Под капотом в скрипте настройка всем знакомого webpack-dev-server, так что не буду на этом останавливаться, подробности можно посмотреть в репозитории.

4ae7e4c371d2434b06ce2797eb43ee12.png

Теперь соберем приложение. Для этого выполним скрипт build, который в свою очередь использует webpack.config.js. Посмотрим на файл:

1b6dbbf0822ed3190d7d14bf6d4f7259.png309f9814e4fdbe7bd37fd1fef2f2d8ee.png

В итоге мы собрали проект. Теперь загрузим его в браузер:

18bbf21a0d989e12d1ce7e6326b54530.png8d2e271011018764bbddeda3025c1bb6.png

Расширение появилось в списке установленных: теперь его можно тестировать, смотреть на возникающие ошибки и проверять корректность отображения иконок в браузере.

Деплой и релиз

Чтобы максимально автоматизировать процесс на Github, с помощью Github Actions настраиваем проверку лицензий, прогон линтеров, сборку проекта и закачку релизов. Так выглядит наша реализация:

1f6a8266e5c00f197735b3b793ca5c2a.png

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

Теперь опубликуем приложение в магазине расширений Chrome:

  • Создаем учетку — за это нужно заплатить 5$. После оплаты попадаем на главную страницу для разработчиков расширений:

911096f7a78d63524209e216f4a7daba.png

  • Нажимаем + Добавить продукт и прикладываем архив приложения.

  • Заполняем форму с описанием: прикладываем лого и картинки расширения, пишем обоснование для каждого из пермишенов из файла manifest.json. Тут есть нюанс: если добавить слишком много пермишенов, время проверки кода может увеличиться либо проект могут вернуть на доработку. Так что лучше сразу удалить избыточные пермишены или прописать подробное обоснование для каждого.

4c274d21f049f3bd8cd0f48b33166380.png

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

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

Итоги

Как мы работаем теперь? Всё стало проще: заходим в расширение и прописываем Request Header, который проставляется во все запросы из браузера:

53a5794bb6ffb856274121a7ce990835.png

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

Итак, наша команды получила опыт создания и публикации расширений, которым я и поделился. В целом процесс разработки похож на обычное React-приложение, кроме нескольких конфигов и процесса релиза. Надеюсь, было полезно) Задавайте вопросы в комментариях, а если хотите поучаствовать в развитии open source, оставляйте предложения по улучшению в issues на GitHub. 

А еще про разработку, open source и облачные сервисы мы будем говорить на конференции GoCloud — приходите или подключайтесь онлайн, если интересно. 

Что еще почитать в блоге:

© Habrahabr.ru