Привет, меня зовут Сергей Цибульченко, я fullstack‑разработчик. На хабре уже есть несколько статей про PWA, но они не раскрывают всех подводных камней данной технологии, я с этим лично столкнулся когда делал пет‑проект по этим статьям.Для начала, что такое PWA? Это веб‑страница которая выглядит и ведёт себя как приложение, но при этом имеет технические ограничения веб‑страницы. Читать далее
Привет, меня зовут Сергей Цибульченко, я fullstack‑разработчик. На хабре уже есть несколько статей про PWA с замахом на исчерпывающее покрытие темы (Разрабатываем PWA. Полная инструкция, PWA вместо приложения: плюсы, минусы и прочие в топе поисковой выдачи Google), но они не раскрывают всех подводных камней данной технологии, я с этим лично столкнулся когда делал пет‑проект по этим статьям. Я не буду рассматривать разработку PWA от начала и до конца, я лишь затрону проблемы, которые возникли у меня в процессе разработки приложения и являются неочевидными и малоосвещенными.
Шаблоны тренировок
Для начала, что такое PWA? Это веб‑страница которая выглядит и ведёт себя как приложение, но при этом имеет технические ограничения веб‑страницы.
Почему я решил написать свою статью о PWAВ апреле 2026 года Google решил что все приложения должны быть подписаны верифицированным аккаунтом Google в консоли разработчика.
Пройти верификацию из РФ не очень уж и просто, а для ряда граждан и юр.лиц невозможно в принципе (упоминание Крыма и новых территорий в документах). Неподписанные приложения просто не будут устанавливаться на Android телефоны с предустановленным Google Play начиная с 2027 года. Статус неподписанных приложений на телефонах Huawei ещё неясен. Из‑за этого PWA становится более актуальной альтернативой особенно в связке с wasm.
Не у всех fullstack‑разработчиков есть возможность и желание приобрести лицензию ios‑разработчика.
Тем не менее, есть потребность сделать кросплатформенный продукт без наличия лицензии, айфона и макбука в обход App Store.
Модерация приложений в RuStore
Некоторые приложения просто не могут пройти модерацию. Например, если вы разрабатываете мессенджер, то с большой долей вероятности на этапе модерации вам откажут. Отдельной болью можно выделить монетизацию для самозанятых и прочие правила и проблемы магазина, а хочется сделать так как надо нам, а не как надо магазинам приложений.
Нет времени учить Kotlin/Flutter и C#
Это хорошо когда можно сделать продукт используя знакомые для веб‑разработчика html/css/js в мобильном приложении.
Крайне слабое представление о PWA у коллег, а тем более, клиентов.
Относительно легко завернуть PWA в Capacitor.
Если в последствии возникнет необходимость (и бюджет) продвигаться через магазины приложений, можно сгенерировать WebView в Capacitor. При условии поддержки offline режима модерация не должна быть проблемой.
Техническое задание на проектПриложение фитнес‑трекер с поддержкой режима offline, то есть offline‑first PWA.
Приложение должно работать на Android и iPhone, в том числе, на старых моделях.
Функционал на уровне известного трекера Strong, где можно создавать шаблоны тренировок, отмечать подходы, сохранять историю тренировок, автоматически отмечать таймстампы начала и окончания подхода (таймеры отдыха), вести статистику прогресса по упражнениям.
Редактируемая из админки на сервере база из 1200 упражнений с анимированными изображениями.
Синхронизация данных с сервером, когда устройство выходит в онлайн, возможность сгенерировать ссылку на завершенную тренировку и поделиться публичной ссылкой.
Простейшая регистрация по email/password просто чтобы у пользователя был id по которому можно синхронизироваться при переносе данных на другое устройство.

Название я удалил чтобы не посчитали скрытой рекламой
Зачем я делал это приложение. «У самурая нет цели, есть только путь». Это пет‑проект в стол для меня лично и небольшого количества моих знакомых, отработка навыков разработки PWA на случай если всё же понадобится делать что‑то масштабное на заказ. Возможно, приложение когда‑нибудь станет публичным. Тем более, что Strong ушел с рынка РФ и не хочется платить за премиум, уж лучше своим пользоваться.
Выбранный стекVue.js (SPA)
bootstrap 5
Dexie.js для работы с indexedDB
Pinia для сохранения состояния при переходе между табами приложения
tanstack/vue‑virtual для виртуальных списков
Laravel/Postgres/Redis для API на бекенде
Я выбрал именно этот стек из‑за того, что я был с ним хорошо знаком (кроме dexie), можете без проблем заменить vue на react и серверную часть на то, чем владеете, тут это не принципиально.
От чего сразу пришлось отказатьсяДанные из фитнес‑браслетов. Конкретно к теме PWA это не имеет отношения, это проблемы моего пет‑проекта, возможно, кому‑то информация будет полезна. Изначально мне очень хотелось вытягивать данные по пульсу, SpO2 и стрессу, накладывать их на график упражнений и отдыха. Но я наткнулся на ряд проблем, некоторые из них оказались непреодолимыми:
Нет единого стандарта данных для wearables. Каждый изготовитель реализует это как хочет. Добавляет проблем, но решаемо, в том числе уже готовыми библиотеками.
Особая категория персональных данных. И хотя фитнес‑браслеты и смарт‑часы не являются медицинскими приборами, не могут использоваться в медицине, данные из них всё равно считаются особой категорией ПД, что тянет за собой целый пласт неприятной бюрократии. В теории, решаемо, но не для пет‑проекта одного разработчика. Часть этой проблемы можно обойти не отправляя данные на сервер, а хранить только на устройстве.
Нежелание изготовителей фитнес‑браслетов предоставлять доступы к SDK физическим и юридическим лицам из РФ. Это обойти я уже был не в силах.
Подробно я не буду их разбирать, только перечислю:
AI мало знакомы с PWA и с indexedDB. Модели будут часто генерировать код, который не будет работать в PWA, постоянно будут пытаться создавать новые версии баз в indexedDB. Необходимо жестко прописать в скилах и гайдлайнах для AI не создавать новые базы и что мы работаем с offline‑first PWA, а не обычным SPA сайтом. Также надо будет чётко прописать где хранятся токены авторизации пользователя, а где всё остальное, иначе модель запутается между localStorage и indexedDB. У любителей LLM ручных правок Ai‑кода будет на порядок больше, ревьювить придётся более пристально.
Если вы раньше не делали frontend для мобильных телефонов, в вёрстке обязательно учитывайте Safe Area всех вариантов комбинаций браузер + ОС чтобы меню или другие UI‑элементы не наехали на камеру мобильника, стандартный эмулятор телефона в Chrome в этом не поможет. Надо тестировать на реальном телефоне или в Android Studio на виртуальных.
Длинные списки из сотен элементов гарантированно повесят телефон, необходимо использовать виртуальные списки
Brainstorm с Ai. Если владеете английским языком, сразу переходите на английский. Иначе на русском DeepSeek и ChatGPT будут выдавать информацию только с Хабра, а про PWA её тут мало.
PWA требует https или localhost и никак иначе. Придется повозиться с самоподписанными сертификатами, обычный npm run dev тут не справится. Некоторые дистрибутивы Linux даже localhost не хотят принимать, им нужен только https.
Из последнего пункта вы наверно уже догадались, что PWA может быть установлен не только на телефон, но и как десктопное приложение, Windows и Ubuntu отлично с ним работают (при наличии в ОС браузера кроме Firefox), пользователь после установки запускает PWA как обычный ярлык. То есть, можно делать на html/css/js кроссплатформенное mobile‑first приложение с поддержкой offline режима.
А теперь, как и обещал, перечислю подводные камни в разработке PWA.
Первый подводный камень. Где хранить динамические данные для offline‑режима?Моя цель была разработать именно offline‑first приложение, привязка работоспособности к наличию связи с сервером это компромисс на который я не был готов идти. И тут возникает закономерный вопрос — где хранить динамические данные? Любой backend‑разрботчик недолго думая ответит: «В webSQL браузера, а картинки можно записывать как blob туда же». Я тоже так думал, но как оказалось, webSQL уже давно deprecated и его в браузерах нет.
После изучения различных технологий, я пришел к выводу, что единственный вариант — indexedDB. Варианты вроде wasm + sqlite я не рассматривал. У indexedDB есть как свои плюсы, так и минусы.
Во‑первых, это nosql‑хранилище где можно хранить документы, создавать индексы, проводить поиск и фильтрцию по значениям документов. Во вторых, на чистом js с indexedDB работать очень сложно, к счастью, есть бесплатная библиотека Dexie (да‑да, аналоги Dexie платные), она серьёзно облегчает взаимодействие с базой и даже предоставляет реактивность для Vue.js или React. Можно по API или в фоновых процессах удалять или сохранять данные, браузер реактивно отреагирует на изменения.
Второй подводный камень. Как в PWA добавить 1200 изображений?В моей базе более тысячи упражнений, у каждого был собственный GIF‑файл и возможность прикрепить видео. Я посмотрел как это реализовано в других фитнес трекерах для Android.
Вариант 1: просто вставить img src="https://адрес_изображения" и оно доступно для просмотра только когда устройство в онлайне.
Вариант 2: изображения добавлены в bundle, являются статическим контентом, скачиваются на телефон вместе с приложением, изменения только при повышении версии приложения.
Первый вариант мне не нравился хотя именно так и поступили разработчики приложения Strong, на которое я ориентировался.
Второй вариант был нерелевантный, я делал PWA, а не Android‑приложение, папки bundle у меня нет. Также, у меня на серверной части упражнения могли как добавляться, так и изменяться, клиент обязан был на это реагировать по API и обновлять локальную базу. То есть, динамический контент, а не статика. Если важность данной проблемы не совсем понятна, представьте, что вы делаете мессенджер и вам надо сохранять большое количество сообщений с пересылаемыми файлами и это должно быть доступно в offline‑режиме для просмотра на обычной веб‑странице. Видео я решил оставить обычным тегом <video> с доступностью только в online‑режиме.
1200 GIF‑картинок суммарно легко перевалили за 350Мб (и это не считая данных о всех тренировках пользователя которые в ту же БД записываются) и меня это не устраивало. Изучив варианты, я написал скрипт на Python чтобы их все перегнать в формат avif. Размер базы упал до 37Мб. Браузер при наличии хорошего сигнала wi‑fi скачивал и локально сохранял их все очень быстро в виде blob.
Четвёртый подводный камень. Сброс кеша браузеромПосле закрытия PWA браузер на своё усмотрение удалял базу из indexedDB, даже 37Мб уже часто вызывали удаление кеша. На этот случай необходимо запрашивать у пользователя разрешение на Persistent Storage. Но полагаться на то, что браузер не прихлопнет БД, всё равно не стоит, необходимо иметь fallback на случай удаления базы и скачивание нужных данных по API с сервера. Это особенно важно для Safari на iPhone, где браузер не сильно уважает мнение как разработчика, так и пользователя, удаление может произойти не сразу, а через пару дней. Также, пользователь может не выдать разрешение на Persistent Storage. Уж лучше в offline‑режиме показать «Необходима синхронизация» чем оставить приложение в нерабочем состоянии.
Пятый подводный камень. NotificationsЕсли вы хотите реализовать уведомления средствами браузера, учтите что это не очень хорошо работает. Во‑первых, пользователь должен дать согласие на показ уведомлений. Пользователь может этого не сделать. Во‑вторых, будут заметные задержки в доставке сообщений, фоновые процессы PWA имеют невысокий приоритет когда PWA свёрнуто или закрыто. Тем не менее вы можете подключить Firebase к своему PWA, просто учтите, что это работает не так надёжно как уведомления нативных приложений.
Итак, нам понадобятся несколько файлов для минимальной работы PWA. Для начала, нужен файл manifest.json.
{ “name”: “MyApp”, “short_name”: “MA”, “icons”: [ { “src”: “/android-chrome-192x192.png”, “sizes”: “192x192”, “type”: “image/png” }, { “src”: “/android-chrome-512x512.png”, “sizes”: “512x512”, “type”: “image/png” } ], “theme_color”: “#ffffff”, “background_color”: “#ffffff”, “display”: “standalone” }
Ещё потребуется сервис воркер. Минимально рабочий файл sw.js выглядит вот так.
self.addEventListener(‘fetch’, (event) => {event.respondWith(fetch(event.request));});
Потом вы его будете наполнять, а точнее вы будете генерировать этот файл с помощью npm‑команд.
И тут начинается интересное. Сервис воркер не будет постоянно работать в фоновом режиме. Не помещайте туда вызовы API и прочие долгоиграющие важные процессы. ОС телефона может убить его в любой момент, гарантируется работоспособность только если все три условия соблюдены:
телефон не заблокирован
PWA запущен
PWA не свёрнут, приложение видно на экране устройства
Запускайте синхронизацию по API только когда пользователь использует PWA и не из sw.js, приоритет у фоновых процессов свёрнутой веб‑страницы меньше по сравнению с фоновыми процессами нативных приложений. Звуковые уведомления работают ожидаемым образом — если пользователь ещё не взаимодействовал с интерфейсом, автовоспроизведение аудио не будет работать. Если же взаимодействовал, то проблем быть не должно сверх тех, что уже есть у вкладки браузера. В моём пет‑проекте звуковые оповещения используются в таймерах отдыха. С таймерами то же самое — отказ от любых setInterval() в пользу setTimeout().
И последнее. Основная проблема PWA на данный момент как раз таки не в разработке, а в продвижении. В РФ нет магазина где можно выложить PWA или TWA, а среднестатистический пользователь никогда в жизни с ним не сталкивался. Придётся продвигать приложение самостоятельно с инструкциями как его устанавливать, для инди‑проектов конкурировать с магазинами приложений — непосильная задача. Если же вы делаете приложение для сотрудников конкретной организации, тут всё намного проще как для вас, так и для организации — разработка с куда более скромным бюджетом в сравнении с Android+IOS в обход магазинов и правил модерации.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Next.js, localStorage в App Router без hydration-ошибок | 0 | 7.17 | 30-07-2026 |
| 2 | Обзор умных часов HUAWEI WATCH FIT 5 Pro: тренируйся, панда! | 2 | 6 | 21-05-2026 |
| 3 | [Перевод] Понятие о конечных автоматах: руководство разработчика по предсказуемой логике приложений | 0 | 9.68 | 29-05-2026 |
| 4 | Обзор HUAWEI Band 11 Pro: то ли еще браслет, то ли уже часы | 0 | 5 | 31-03-2026 |
| 5 | Fitbit Charge 6 im Hardware-Ökosystem: Verbindung, Sensoren und das richtige Armband | 0 | 5 | 13-07-2026 |
| 6 | Книга: «Фулстек JavaScript: Секреты, которые должен знать каждый мидл» | 0 | 10.32 | 12-05-2026 |
| 7 | [Перевод] Пишем движок для JavaScript с нуля | 0 | 6.36 | 09-06-2026 |
| 8 | React Update Lifecycle: что происходит при обновлении компонента | 0 | 8.86 | 05-08-2026 |
| 9 | AAA Движок для GTA San Andreas в браузере и переписывание ThreeJS | 0 | 6.18 | 04-08-2026 |
| 10 | MWC 2015: 7 самых интересных носимых устройств выставки | 0 | 5 | 04-03-2015 |