Как разработать браузерное расширение в Chrome на React: разбираем на примере Cloudhood
Всем привет! Меня зовут Егор, я технический лидер в команде, которая занимается разработкой личного кабинета Cloud.ru. Некоторое время назад загорелись идеей сделать браузерное расширение на React, но не было хороших гайдов (либо я их не нашел), поэтому в итоге написали свой) В статье поделился нашим опытом и наработками, а также рассказал, зачем мы вообще взялись за этот open source проект.
Чем полезны браузерные расширения? В статье не буду разбирать их «минусы» и «плюсы». Скажу только, что расширения позволяют делать полезные инструменты как для повседневного использования, так и для решения рабочих задач. Вот несколько примеров:
блокировка всплывающей рекламы,
видоизменение html-страниц,
подмена тела и заголовков запросов и ответов,
создание технических тулзов для библиотек в разработке.
И многое-многое другое) Все ограничено только вашей фантазией и API браузера. Мы, например, решили сделать расширение, которое помогает при разработке приложений, когда много команд параллельно пилит свои фичи.
Зачем мы этим занялись
В первую очередь, чтобы иметь стабильную среду для разработки и параллельно тестировать доработки в одних и тех же проектах. Хоть процесс у нас и построен по принципу микрофронтендов, о чем рассказывал Михаил Трифонов в другой статье, это решало только часть проблем.
Кратко расскажу про наш CI/CD:
Сначала разработчик делает фичу.
Затем пушит код в GitLab.
В GitLab запускаются пайплайны, прогоняются различные проверки кода, затем собирается приложение и в Docker деплоится на dev-стенд.
Кажется, все готово. Остается только понять, как разным командам одновременно иметь свою версию приложения на dev-стенде для тестирования фич? Тут и приходит на выручку наше расширение Cloudhood. Теперь с помощью прокидывания хедеров в запросы мы можем подтягивать конкретную версию проекта (как у нас всё реализовано можно почитать в статье).
Конфигурация расширения
Чтобы сконфигурировать приложение, сначала сделаем стандартную структуру проекта.
Пример структуры проекта
Для этого:
Создаем package.json.
Делаем README.md с подробным описанием.
Добавляем конфиги линтера, претира и тайпскрипта.
Настраиваем husky и lint-staged для прекоммит-хуков, в которых будем прогонять линтер и претир.
Добавляем .gitignore — чтобы лишнего в репозиторий не летело.
Выбираем лицензию — нам лучше всего подошла Apache License 2.0.
Подбираем удобную структуру проекта. Мы использовали Feature-Sliced Design:
Теперь добавим файл manifest.json — он понадобится для публикации расширения:
Вот из чего он состоит:
manifest_version — версия файла. Лучше использовать v3, поскольку Chrome постепенно уходит от v2.
icon и default_icon — чтобы Chrome понимал, какая иконка будет отображаться в браузере у всплывающего окна расширения:
default_popup — html или попап, который будет отрисовываться при клике на иконку расширения (т. е. по сути это визуальная часть нашего расширения).
icons — чтобы расширение отображалось на разных страницах интернет-магазина Chrome. Каждая страница имеет свои размеры, например:
host_permissions — помогает определить, на каких сайтах будет работать расширение (наше работает на любом).
permissions — описываем те АPI Chrome, которые планируем использовать. Важный момент: если забыть какой-то из пермишенов, то при установке расширения он не сработает. Почему так? Во-первых, для безопасности пользователей (потенциально расширения могут воровать данные пользователей и делать другие нехорошие вещи). Во-вторых, чтобы Chrome проще было проверять, для чего вы планируете их использовать (нужно написать обоснование для каждого).
background — для работы сервис-воркера (как именно он работает рассмотрим позже).
Вот и всё, переходим к написанию компонентов.
API Chrome: пример использования
Итак, у нас есть сверстанное приложение. Посмотрим, как использовать API Chrome для взаимодействия с сайтом.
Мы в расширении используем его для видоизменения запросов. Вот как это работает:
Используем storage.local для сохранения хедеров для запросов при их удалении, изменении и добавлении. По сути, это аналог localStorage браузера:
Затем сообщаем cервис-воркеру, о том, что нужно обновить хедеры с помощью chrome.runtime.sendMessage:
Если сообщение пришло, он достает из стораджа сохраненные значения хедеров и затем применяет новые хедеры ко всем запросам браузера с помощью API chrome.declarativeNetRequest.updateDynamicRules:
Для чего нужен background.ts разобрались, переходим к запуску и сборке.
Запуск и сборка
Для запуска и настройки приложения React-проект можно запустить локально либо собрать приложение и загрузить в браузер. Первый способ удобнее: так лучше работает hot-reload и проще дебажить.
Поэтому добавим скрипт start в package.json, как показано на скрине. Под капотом в скрипте настройка всем знакомого webpack-dev-server, так что не буду на этом останавливаться, подробности можно посмотреть в репозитории.
Теперь соберем приложение. Для этого выполним скрипт build, который в свою очередь использует webpack.config.js. Посмотрим на файл:
В итоге мы собрали проект. Теперь загрузим его в браузер:
Расширение появилось в списке установленных: теперь его можно тестировать, смотреть на возникающие ошибки и проверять корректность отображения иконок в браузере.
Деплой и релиз
Чтобы максимально автоматизировать процесс на Github, с помощью Github Actions настраиваем проверку лицензий, прогон линтеров, сборку проекта и закачку релизов. Так выглядит наша реализация:
После того, как запушили расширение на Github и залили в основную ветку, следует прогон пайплайнов. В результате получаем собранный проект расширения и скачиваем его.
Теперь опубликуем приложение в магазине расширений Chrome:
Создаем учетку — за это нужно заплатить 5$. После оплаты попадаем на главную страницу для разработчиков расширений:
Нажимаем + Добавить продукт и прикладываем архив приложения.
Заполняем форму с описанием: прикладываем лого и картинки расширения, пишем обоснование для каждого из пермишенов из файла manifest.json. Тут есть нюанс: если добавить слишком много пермишенов, время проверки кода может увеличиться либо проект могут вернуть на доработку. Так что лучше сразу удалить избыточные пермишены или прописать подробное обоснование для каждого.
Отправляем расширение на проверку. Обычно пишут, что проверка занимает несколько недель, но у нас она прошла за пару дней. Проверка новых версий проходила еще быстрее — за один-два часа. Обновить расширение в браузере можно вручную через магазин либо подождать автоматического обновления в течение одного-двух дней.
Также в личном кабинете разработчика есть неплохая аналитика, которая помогает отслеживать, сколько раз расширение просмотрели, скачали, удалили и т. п.
Итоги
Как мы работаем теперь? Всё стало проще: заходим в расширение и прописываем Request Header, который проставляется во все запросы из браузера:
По названию и значению этого хедера оркестратор приложений понимает, какую конкретно версию и какого проекта нужно отдать. Поэтому мы можем независимо от других команд на dev-среде открывать и тестировать нужную нам доработку, прописывать нужные версии микрофронтов и микросервисов. За счет этого поддерживается стабильность одной среды, параллелизация разработки и тестирования.
Итак, наша команды получила опыт создания и публикации расширений, которым я и поделился. В целом процесс разработки похож на обычное React-приложение, кроме нескольких конфигов и процесса релиза. Надеюсь, было полезно) Задавайте вопросы в комментариях, а если хотите поучаствовать в развитии open source, оставляйте предложения по улучшению в issues на GitHub.
А еще про разработку, open source и облачные сервисы мы будем говорить на конференции GoCloud — приходите или подключайтесь онлайн, если интересно.
Что еще почитать в блоге: