PWA (Progressive Web App) — это веб-приложение, которое использует современные возможности браузера, чтобы работать как нативное приложение: быстро загружается, работает офлайн, отправляет push-уведомления и устанавливается на главный экран. В основе PWA лежат три технологии: Service Worker, манифест и HTTPS. Service Worker — это скрипт, который браузер запускает в фоне, отдельно от веб-страницы, открывая возможности, недоступные обычным сайтам: перехват сетевых запросов, кэширование ресурсов, фоновая синхронизация и push-уведомления. Манифест — JSON-файл, который сообщает браузеру, как должно выглядеть приложение при установке на устройство: имя, иконки, цвет темы, ориентация экрана. Без HTTPS Service Worker не работает — это обязательное требование безопасности. PWA совместимы с любым современным браузером и не требуют публикации в магазине приложений. Пользователь открывает сайт, видит предложение установить приложение, нажимает «Добавить на главный экран» — и приложение готово. Для бизнеса PWA означает охват любой аудитории без затрат на разработку под iOS и Android отдельно, высокую скорость загрузки даже на медленных сетях и вовлечение через push-уведомления. В статье разберём, как устроен Service Worker, как собрать манифест, какие стратегии кэширования использовать и как делать офлайн-доступ.
Главное
Главное
Progressive Web App (PWA) — это подход к разработке веб-приложений, который сочетает лучшие качества веба и нативных приложений. Термин ввёл Франческо Берриман (Francesco Berriman) в 2015 году, а Google активно продвигает PWA как замену простым мобильным приложениям. Основная идея: веб-сайт постепенно улучшается в зависимости от возможностей браузера и устройства. Если браузер поддерживает Service Worker — подключается офлайн-режим. Если поддерживает манифест — появляется кнопка «Установить». Если есть API уведомлений — приложение может присылать push. Пользователь на слабом устройстве с медленным интернетом получит базовую версию сайта, а на современном смартфоне — полноценное приложение с иконкой на рабочем столе.
PWA не нужно устанавливать через магазин приложений. Достаточно ссылки. Это решает главную проблему мобильных приложений — высокий порог входа. Пользователь не идёт в Google Play или App Store, не ждёт загрузки в 100 МБ, не даёт десяток разрешений. Он просто открывает сайт, и браузер предлагает установить приложение. Если пользователь отказывается — он всё равно пользуется сайтом. А если соглашается — иконка появляется на главном экране, и приложение открывается как нативное: без адресной строки браузера, с полноэкранным режимом и быстрым запуском.
Технически PWA обязано соответствовать трём критериям: работать по HTTPS (безопасное соединение), иметь Service Worker (скрипт фоновой обработки) и содержать манифест (Web App Manifest). Эти три компонента превращают обычный сайт в устанавливаемое приложение.
Service Worker — это JavaScript-скрипт, который браузер запускает в отдельном потоке, независимо от веб-страницы. Он не имеет доступа к DOM, не может напрямую взаимодействовать с интерфейсом, но может перехватывать и модифицировать сетевые запросы, управлять кэшем, обрабатывать push-уведомления и выполнять фоновую синхронизацию. Service Worker работает как прокси-сервер между браузером и сетью: каждый запрос от страницы проходит через Service Worker, который решает — отдать данные из кэша, запросить с сервера или сделать и то, и другое.
Service Worker живёт по собственному жизненному циклу, не привязанному к открытой странице. Он устанавливается, активируется и работает в фоне даже после закрытия всех вкладок сайта. Это позволяет PWA получать push-уведомления и обновлять кэш в фоновом режиме, без участия пользователя.
Service Worker проходит четыре стадии: регистрация, установка, активация и простаивание/завершение.
Регистрация. Браузер получает ссылку на JS-файл Service Worker и начинает загрузку. Регистрация выполняется из обычного JavaScript на странице:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('SW registered:', registration.scope);
})
.catch(error => {
console.log('SW registration failed:', error);
});
}Установка. После успешной загрузки браузер запускает событие install. На этом этапе принято кэшировать ключевые ресурсы (HTML-каркас, CSS, основные изображения), чтобы приложение могло работать офлайн. Если установка прошла успешно, Service Worker переходит к следующему этапу. Если скрипт выбросил ошибку — установка отменяется, и старый Service Worker продолжает работать.
self.addEventListener('install', event => {
event.waitUntil(
caches.open('pwa-cache-v1').then(cache => {
return cache.addAll([
'/',
'/index.html',
'/styles/main.css',
'/scripts/app.js',
'/images/logo.png'
]);
})
);
});Активация. Когда все вкладки сайта закрыты, старый Service Worker завершается, и новый активируется. Событие activate используется для очистки старых кэшей и миграции данных:
self.addEventListener('activate', event => {
const cacheWhitelist = ['pwa-cache-v2'];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (!cacheWhitelist.includes(cacheName)) {
return caches.delete(cacheName);
}
})
);
})
);
});Простаивание. Активированный Service Worker перехватывает запросы и обрабатывает события. Он может быть остановлен браузером для экономии ресурсов и перезапущен при следующем запросе. Service Worker — событийно-ориентирован: он живёт только пока обрабатывает события.
Service Worker поддерживает несколько стратегий кэширования. Выбор зависит от типа ресурса и требований к актуальности данных.
Cache First (сначала кэш). Сначала проверяется кэш. Если ресурс найден — возвращается из кэша. Если нет — запрос уходит на сервер, ответ сохраняется в кэш. Стратегия подходит для статических ресурсов: изображения, шрифты, CSS, JS. Обеспечивает максимальную скорость и полноценную работу офлайн.
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request).then(fetchResponse => {
return caches.open('pwa-cache-v1').then(cache => {
cache.put(event.request, fetchResponse.clone());
return fetchResponse;
});
});
})
);
});Network First (сначала сеть). Запрос сначала уходит на сервер. Если сервер ответил — данные возвращаются пользователю и сохраняются в кэш. Если сервер недоступен — отдаётся кэшированная копия. Стратегия подходит для API-запросов, новостей, контента, который должен быть актуальным, но может работать и офлайн.
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request).then(response => {
return caches.open('pwa-dynamic-v1').then(cache => {
cache.put(event.request, response.clone());
return response;
});
}).catch(() => {
return caches.match(event.request);
})
);
});Stale While Revalidate (устаревшее с обновлением). Ответ немедленно возвращается из кэша, а в фоне отправляется запрос на сервер для обновления кэша. Пользователь видит контент мгновенно, а следующий запрос получит уже свежие данные. Идеально для контента, который меняется, но не критично, если пользователь увидит версию на пару минут старше.
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cached => {
const fetchPromise = fetch(event.request).then(response => {
return caches.open('pwa-dynamic-v1').then(cache => {
cache.put(event.request, response.clone());
return response;
});
});
return cached || fetchPromise;
})
);
});Манифест — это JSON-файл, обычно manifest.json, который подключается через тег <link> в head HTML-страницы. Он сообщает браузеру, как приложение должно отображаться при установке на устройство: название, иконки разных размеров, цвет фона, цвет темы, ориентацию и режим отображения (браузер, минимальный UI, полноэкранный).
{
"name": "PWA Demo App",
"short_name": "PWA Demo",
"description": "Демонстрационное PWA-приложение",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#3f51b5",
"orientation": "portrait",
"icons": [
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}Параметр display определяет, как будет выглядеть приложение после установки. standalone скрывает адресную строку браузера и панель навигации — приложение выглядит как нативное. fullscreen использует весь экран без каких-либо элементов браузера. minimal-ui оставляет минимальные элементы браузера (кнопки назад и обновления).
Иконки должны быть подготовлены минимум в двух размерах: 192x192 и 512x512 пикселей. Формат PNG с поддержкой параметра purpose (например, maskable) позволяет браузеру адаптировать иконку под форму оболочки устройства — на Android иконка может обрезаться до скруглённого квадрата. После подключения манифеста браузер Chrome на Android начинает показывать баннер «Добавить на главный экран» при выполнении условий: HTTPS, зарегистрированный Service Worker, корректный манифест и минимум два посещения с интервалом 5 минут.
PWA может отправлять push-уведомления как нативное приложение. Для этого используются два API: Push API (серверная отправка) и Notification API (отображение на устройстве). Service Worker получает push-событие даже если приложение не открыто, и может показать уведомление с заголовком, текстом и иконкой.
Для получения push-уведомлений пользователь должен дать разрешение:
Notification.requestPermission().then(permission => {
if (permission === 'granted') {
console.log('Push allowed');
}
});Service Worker подписывается на push-событие и отображает уведомление:
self.addEventListener('push', event => {
const data = event.data.json();
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icons/icon-192x192.png',
badge: '/icons/badge-72x72.png',
actions: [
{ action: 'open', title: 'Открыть' },
{ action: 'close', title: 'Закрыть' }
]
});
});
self.addEventListener('notificationclick', event => {
event.notification.close();
if (event.action === 'open') {
clients.openWindow('/');
}
});Для отправки push с сервера используется протокол Web Push (VAPID). Сервер шифрует сообщение и отправляет его через браузерный push-сервис (Firebase Cloud Messaging для Chrome, Mozilla Autopush для Firefox). Подписку пользователя (объект PushSubscription) необходимо сохранять на сервере при регистрации.
PWA должны быть быстрыми. Google Lighthouse — инструмент аудита, который проверяет PWA по контрольному списку: регистрация Service Worker, ответ 200 при офлайн-доступе, корректный манифест, HTTPS, правильный размер иконок, быстрая загрузка на медленных сетях. Минимальный проходной балл для PWA — 80 из 100 по шкале Lighthouse. Приложения с баллом ниже 80 не получают баннер установки в Chrome.
Ключевые метрики производительности для PWA — First Contentful Paint (FCP) до 1.8 секунды, Largest Contentful Paint (LCP) до 2.5 секунды, Time to Interactive (TTI) до 3.5 секунды. Service Worker может значительно улучшить FCP и LCP, если кэширует критический HTML и CSS при установке. Стратегия Cache First для статики и Stale While Revalidate для API даёт лучший пользовательский опыт.
Одна из главных возможностей PWA — работа без интернета. Service Worker перехватывает запросы и отдаёт кэшированные версии страниц. Если пользователь пытается открыть страницу, которой нет в кэше, можно показать кастомную страницу-заглушку:
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
}).catch(() => {
return caches.match('/offline.html');
})
);
});Фоновая синхронизация (Background Sync) позволяет отложить отправку данных на сервер, если устройство офлайн. Service Worker сохраняет запрос и автоматически отправляет его, когда соединение восстанавливается:
self.addEventListener('sync', event => {
if (event.tag === 'sync-messages') {
event.waitUntil(sendMessages());
}
});
function sendMessages() {
return caches.open('pwa-pending-v1').then(cache => {
return cache.keys().then(requests => {
return Promise.all(requests.map(request => {
return fetch(request).then(() => cache.delete(request));
}));
});
});
}Chrome DevTools предоставляет панель Application, где можно отлаживать Service Worker (регистрация, статус, кэш, push), проверять манифест, очищать кэш и создавать собственные push-уведомления для тестирования. Вкладка Network с эмуляцией офлайн-режима помогает проверить работу кэша. Lighthouse в панели Audits даёт полный PWA-аудит с рекомендациями.
Workbox — библиотека от Google, которая упрощает создание Service Worker. Вместо ручного написания стратегий кэширования, Workbox предлагает готовые модули:
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';
precacheAndRoute(self.__WB_MANIFEST);
registerRoute(
/\.(?:png|jpg|jpeg|svg|gif)$/,
new StaleWhileRevalidate()
);Workbox интегрируется со сборщиками (Webpack, Vite) и автоматически генерирует манифест предварительного кэширования. PWA Builder (pwabuilder.com) от Microsoft генерирует все файлы PWA онлайн: Service Worker, манифест, иконки, HTML-шаблон.
FAQ
PWA — это сайт, который после установки на телефон работает как обычное приложение: есть иконка на рабочем столе, офлайн-доступ, push-уведомления, полноэкранный режим без адресной строки браузера. При этом не нужно идти в App Store или Google Play — установка происходит в один клик из браузера.
Нативное приложение пишется под конкретную платформу (Swift для iOS, Kotlin для Android), требует публикации в магазине и установки вручную. PWA — это веб-сайт, написанный на HTML, CSS и JavaScript, который может быть установлен на устройство через браузер. PWA не занимает много места (только кэш), обновляется автоматически и поддерживается всеми современными браузерами.
Chrome, Firefox, Safari (начиная с iOS 11.3), Edge, Samsung Internet, Opera. Service Worker поддерживается всеми современными браузерами, кроме Internet Explorer. Safari имеет ограниченную поддержку push-уведомлений, но базовые возможности PWA (установка на главный экран, офлайн-доступ) работают.
Да. Для этого нужно добавить манифест (manifest.json), создать и зарегистрировать Service Worker (sw.js), перевести сайт на HTTPS, добавить мета-теги и иконки. Сам сайт не требует переписывания, если он работает на JavaScript и отдаёт HTML. Если сайт полностью серверный (MPA), можно добавить Service Worker как слой кэширования без изменения архитектуры.
Когда пользователь открывает PWA, браузер проверяет, не изменился ли файл Service Worker на сервере. Если файл отличается хотя бы на байт, браузер загружает новый Service Worker и запускает его установку. Старый Service Worker продолжает работать, пока все вкладки приложения не будут закрыты. После закрытия вкладок новый Service Worker активируется и заменяет старый. Пользователь может принудительно обновить приложение, перезагрузив страницу.
PWA оправдано, если задача — охватить максимальную аудиторию без затрат на разработку под две платформы, если приложение не требует глубокого доступа к оборудованию (Bluetooth, NFC, камера в фоне), если контент меняется часто. Для сложных приложений (игры, редакторы видео) нативное приложение остаётся предпочтительным. PWA часто используется как дополнительный канал или как MVP перед полноценным приложением.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также