Разработка PWA-приложения на базе 1С-Битрикс
С версии 21.0 в Битриксе появился штатный модуль pwa — генерация manifest.json, регистрация Service Worker, базовый offline-режим. На демо выглядит убедительно: сайт добавляется на рабочий стол, работает без сети, показывает пуш-уведомления. На практике — модуль покрывает 40% задач, остальное требует ручной настройки: стратегии кеширования, взаимодействие с composite, обход ограничений iOS, тюнинг Lighthouse-метрик до зелёной зоны.
Что даёт штатный модуль pwa
Модуль включается через Административный раздел → Настройки → Настройки модулей → PWA. После активации Битрикс делает три вещи:
- Генерирует
/manifest.jsonс параметрами из настроек модуля —name,short_name,start_url,display,theme_color,background_color, набор иконок изicons[] - Регистрирует Service Worker
/sw.jsчерезnavigator.serviceWorker.register('/sw.js')вtemplate_styles.phpили через<script>в шаблоне сайта - Добавляет
<link rel="manifest" href="/manifest.json">в<head>
Параметр display по умолчанию standalone — приложение открывается без адресной строки браузера. Для сайтов, где пользователю нужна навигация назад через историю браузера, ставим minimal-ui.
Иконки — модуль требует набор: 72x72, 96x96, 128x128, 144x144, 152x152, 192x192, 384x384, 512x512. Формат PNG. Если не загрузить 512x512 — Lighthouse снимает баллы за «installability». Генерируем через CFile::ResizeImageGet() из одного исходника, но проверяем качество вручную — автоматическое сжатие иногда даёт артефакты на мелких размерах.
Service Worker: стратегии кеширования
Штатный /sw.js от Битрикса использует стратегию Cache First для статических ресурсов и Network First для HTML-страниц. Этого достаточно для базового offline, но не для реального PWA-поведения.
Cache First — Service Worker ищет ответ в CacheStorage, если находит — возвращает, не обращаясь к серверу. Работает для /bitrix/js/, /bitrix/css/, /upload/. Проблема: при обновлении JS-бандла пользователь продолжает получать старую версию, пока кеш не истечёт или не обновится через skipWaiting().
Network First — сначала запрос к серверу, если сервер недоступен — fallback из кеша. Для HTML-страниц это правильная стратегия, но есть нюанс с модулем composite. Композитный сайт отдаёт статический HTML с динамическими вставками через AJAX. Service Worker перехватывает уже готовый композитный HTML — и кеширует его. Если динамические вставки зависят от авторизации, в кеш попадает обезличенная версия, а после восстановления сети пользователь видит чужие данные. Решение — исключаем из кеширования URL с параметром sessid и запросы к bitrix/services/main/ajax.php.
Stale While Revalidate — третья стратегия, которую штатный модуль не использует, но мы добавляем для API-ответов. Отдаём из кеша мгновенно, параллельно обновляем кеш запросом к серверу. Идеально для каталога, где данные меняются раз в час, а пользователь не должен ждать.
Кастомную логику кеширования пишем в /local/templates/main/sw-custom.js и подключаем через importScripts() в основном Service Worker. Не модифицируем /sw.js напрямую — он перегенерируется модулем.
Взаимодействие с модулем composite
Модуль composite и PWA решают одну задачу — ускорить первый рендер. Но работают по-разному и могут конфликтовать.
composite генерирует статический HTML и сохраняет в файловую систему (/bitrix/html_pages/). Nginx отдаёт файл напрямую, минуя PHP. Время ответа — 5-15ms. Динамические зоны (<div id="bx-composite-...">) подгружаются отдельным AJAX-запросом после загрузки страницы.
Когда поверх composite включается Service Worker с Cache First для HTML — Nginx даже не получает запрос, ответ идёт из CacheStorage браузера. Это быстрее, но есть ловушка: если composite обновил HTML (товар закончился, поменялась цена), Service Worker продолжает отдавать старую версию.
Решение — версионирование кеша. В manifest.json добавляем кастомное поле sw_version, при каждом деплое инкрементируем. Service Worker при активации сравнивает версию и очищает старый кеш:
const CACHE_VERSION = 'v1.4';
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== CACHE_VERSION).map(k => caches.delete(k)))
)
);
});
Push-уведомления через модуль pull
Битриксовский модуль pull (Push and Pull) обеспечивает real-time взаимодействие: чат, уведомления, обновления данных. Для PWA он же используется как транспорт пуш-уведомлений.
Архитектура: клиент подписывается через PushSubscription API → отправляет endpoint, keys.p256dh, keys.auth на сервер → сервер сохраняет подписку в b_pull_push → при наступлении события отправляет payload через \Bitrix\Pull\Push::send().
Настройка:
- Генерируем VAPID-ключи через
web-pushбиблиотеку или через openssl:openssl ecparam -genkey -name prime256v1 -out private.pem - Публичный ключ прописываем в настройках модуля
pull→ «Push-уведомления» → «VAPID Public Key» - В Service Worker обрабатываем событие
push:
self.addEventListener('push', event => {
const data = event.data.json();
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: '/local/templates/main/img/icon-192.png',
data: { url: data.url }
})
);
});
Ограничение iOS: Safari на iOS поддерживает Web Push только с версии 16.4, и только для сайтов, добавленных на домашний экран. PushManager.subscribe() в обычной вкладке Safari вернёт ошибку. Проверяем поддержку:
if ('PushManager' in window && 'Notification' in window) {
// подписка возможна
}
Lighthouse PWA Audit: что проверяется
Lighthouse оценивает PWA по чек-листу. Вот что требует внимания на Битриксе:
-
Installable — корректный
manifest.json, Service Worker с обработчикомfetch, иконка 512x512,start_urlотвечает 200 в offline. Штатный модуль закрывает, если не забыть иконку -
Optimized — HTTPS обязательно,
<meta name="theme-color">совпадает сtheme_colorв манифесте, редирект с HTTP на HTTPS. В Битриксе HTTPS настраивается черезНастройки→Главный модуль→ «Работать в режиме HTTPS» -
Offline — при отключении сети пользователь видит кастомную страницу, а не стандартный Chrome Dinosaur. Создаём
/offline.htmlи кешируем при установке Service Worker
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_VERSION).then(cache =>
cache.addAll(['/', '/offline.html', '/local/templates/main/css/style.min.css'])
)
);
});
-
Redirects —
start_urlне должен возвращать 3xx. Если Битрикс настроен на редирект/→/ru/для мультиязычного сайта, тоstart_urlв манифесте указываем/ru/, а не/
Событие beforeinstallprompt
Браузер показывает баннер установки PWA автоматически, но момент выбирает сам — обычно через 30 секунд после загрузки. Мы перехватываем событие и показываем кастомную кнопку:
let deferredPrompt;
window.addEventListener('beforeinstallprompt', e => {
e.preventDefault();
deferredPrompt = e;
document.getElementById('install-btn').style.display = 'block';
});
document.getElementById('install-btn').addEventListener('click', () => {
deferredPrompt.prompt();
});
На iOS событие beforeinstallprompt не поддерживается. Для Safari показываем инструкцию: «Нажмите Поделиться → Добавить на экран «Домой»». Определяем iOS через navigator.userAgent — не идеально, но альтернатив нет.
PWA vs нативное приложение на Битриксе
| Критерий | PWA | Нативное (iOS/Android) |
|---|---|---|
| Установка | Из браузера, без App Store | App Store / Google Play |
| Обновление | Автоматическое через Service Worker | Через магазин, требует подтверждения |
| Доступ к устройству | Камера, геолокация, уведомления | Полный доступ к API устройства |
| Offline | Кешированный контент | Полноценная offline-логика |
| Размер | 0 MB (кеш браузера) | 20-100+ MB |
| Стоимость разработки | 1-2 недели поверх сайта | 2-4 месяца отдельный проект |
Для каталога, корпоративного сайта, новостного портала — PWA достаточно. Для приложения с тяжёлой офлайн-логикой, Bluetooth, NFC — нужен нативный клиент.
Этапы работ
-
Аудит текущего сайта (2-3 дня) — проверка совместимости шаблона с PWA, анализ Lighthouse, оценка взаимодействия с
composite -
Настройка модуля и Service Worker (3-5 дней) — конфигурация
manifest.json, стратегии кеширования, offline-страница, push-подписка - Тестирование (2-3 дня) — проверка на Android (Chrome, Samsung Internet), iOS (Safari), десктоп (Chrome, Edge). Lighthouse audit на каждом этапе
- Запуск и мониторинг (1-2 дня) — деплой, проверка метрик, настройка алертов на ошибки Service Worker
| Масштаб | Сроки |
|---|---|
PWA для существующего сайта с composite |
1-2 недели |
| PWA + push-уведомления + offline-каталог | 2-3 недели |
| PWA с кастомным App Shell и сложной offline-логикой | 3-5 недель |
Ограничения, о которых важно знать
- iOS Safari не поддерживает Background Sync, Badge API, полноценные push без добавления на Home Screen
-
Обновление Service Worker — даже при
skipWaiting()пользователь получит новую версию только при следующем открытии. Нет способа заставить обновление мгновенно -
Размер кеша — браузеры ограничивают
CacheStorage. Chrome выделяет до 80% свободного места на диске, Safari — 50MB на origin. Кешировать весь каталог на 10К товаров не получится — только критические ресурсы -
Модуль
compositeпри включённом CDN может отдавать HTML с абсолютными URL CDN-домена — Service Worker не перехватит запросы к другому origin. Проверяем черезscopeвregister()







