Как создать PWA: полное руководство от основ до Service Workers
#Веб-разработка #Основы JavaScript #Web APIДля кого эта статья:
- Фронтенд-разработчики и веб-разработчики всех уровней
- Менеджеры проектов и владельцы бизнеса, заинтересованные в оптимизации приложений
- Студенты и начинающие разработчики, изучающие современные веб-технологии
Когда я впервые запустил PWA-приложение на смартфоне клиента и оно продолжало работать в метро без интернета, его реакция была бесценна: "Это точно веб-сайт, а не нативное приложение?" 🚀 Прогрессивные веб-приложения изменили правила игры в мире веб-разработки, объединив лучшие качества веб-сайтов и нативных приложений в одно целое. Даже технологические гиганты вроде Twitter и Starbucks перешли на PWA, увеличив конверсию и снизив затраты на разработку. Независимо от вашего уровня – фронтендер с опытом или начинающий разработчик – это руководство проведет вас через все этапы создания полноценного PWA, от базового манифеста до продвинутых Service Workers.
Что такое PWA и почему они покоряют мир веб-разработки
Progressive Web Applications (PWA) представляют собой веб-приложения, которые используют современные веб-возможности для предоставления пользователям опыта, сравнимого с нативными приложениями. Они работают в браузере, но при этом могут быть установлены на домашний экран устройства, отправлять пуш-уведомления и функционировать офлайн. 💼
Андрей Соколов, технический директор
Когда наша компания столкнулась с дилеммой: разрабатывать нативные приложения для iOS и Android или искать альтернативное решение, бюджет был ограничен. Клиент — сеть ресторанов быстрого питания — требовал мобильное приложение с возможностью офлайн-просмотра меню и купонов. Разработка двух нативных приложений обошлась бы минимум в 2,5 млн рублей.
Мы предложили PWA-решение. Скептицизм клиента сменился восторгом, когда мы продемонстрировали прототип через три недели вместо обещанных шести для нативной разработки. Приложение работало офлайн, отправляло уведомления о специальных предложениях и загружалось за 2 секунды даже на бюджетных устройствах.
Результат превзошел ожидания: конверсия заказов выросла на 28%, а расходы на разработку сократились на 70% по сравнению с нативными приложениями. Сейчас, спустя полтора года, PWA остается основным каналом мобильных продаж компании.
Ключевые преимущества PWA перед традиционными веб-сайтами и нативными приложениями:
| Характеристика | Обычный веб-сайт | PWA | Нативное приложение |
|---|---|---|---|
| Установка на устройство | Нет | Да | Да |
| Работа офлайн | Нет | Да | Да |
| Доступ к API устройства | Ограниченный | Расширенный | Полный |
| Пуш-уведомления | Нет | Да | Да |
| Стоимость разработки | Низкая | Средняя | Высокая |
| Обновление | Мгновенное | Мгновенное | Через магазин приложений |
Статистика внедрения PWA говорит сама за себя:
- Twitter Lite (PWA) уменьшил использование данных на 70%, а время загрузки на 30%
- Pinterest увеличил время, проведенное на сайте, на 40% после внедрения PWA
- Alibaba увеличил конверсию новых пользователей на 104% с помощью своего PWA
Технологический стек PWA базируется на трех ключевых компонентах:
- Web App Manifest — JSON-файл, который содержит информацию о приложении (название, иконки, цвета и т.д.)
- Service Workers — JavaScript-файлы, работающие в фоновом режиме и обеспечивающие офлайн-функциональность, кеширование и пуш-уведомления
- Application Shell Architecture — подход к структурированию приложения, обеспечивающий мгновенную загрузку интерфейса

Основы создания PWA: манифест и архитектура приложения
Создание PWA начинается с разработки базовой структуры и Web App Manifest. Этот JSON-файл предоставляет браузеру информацию о вашем приложении и определяет, как оно будет отображаться при установке на устройство пользователя. 📱
Сначала создадим базовую структуру проекта:
/pwa-project
├── index.html
├── css/
│ └── styles.css
├── js/
│ ├── app.js
│ └── service-worker.js
├── images/
│ ├── icons/
│ └── content/
└── manifest.json
Теперь рассмотрим создание манифеста приложения. Вот пример базового manifest.json:
{
"name": "Мое PWA Приложение",
"short_name": "PWA Демо",
"description": "Демонстрационное прогрессивное веб-приложение",
"start_url": "/index.html",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#2196f3",
"orientation": "portrait-primary",
"icons": [
{
"src": "/images/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/images/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/images/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/images/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/images/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "/images/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/images/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/images/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Параметры манифеста и их значение:
| Параметр | Описание | Влияние на UX |
|---|---|---|
| name | Полное название приложения | Отображается на экране загрузки |
| short_name | Краткое название | Показывается под иконкой на домашнем экране |
| start_url | Начальная страница при запуске | Определяет точку входа в приложение |
| display | Режим отображения (standalone, fullscreen, minimal-ui, browser) | Влияет на наличие элементов браузера в UI |
| background_color | Цвет фона экрана загрузки | Создает плавный визуальный переход при запуске |
| theme_color | Основной цвет приложения | Влияет на цвет строки состояния и интерфейса |
| icons | Массив иконок разных размеров | Обеспечивает корректное отображение на разных устройствах |
Для подключения манифеста к HTML-документу добавьте следующую строку в <head> вашего index.html:
<link rel="manifest" href="/manifest.json">
Также рекомендуется добавить мета-теги для улучшения интеграции с устройствами iOS, которые пока не полностью поддерживают веб-манифесты:
<!-- iOS support -->
<link rel="apple-touch-icon" href="/images/icons/icon-192x192.png">
<meta name="apple-mobile-web-app-status-bar" content="#2196f3">
Архитектура PWA обычно следует модели Application Shell (оболочка приложения). Эта модель разделяет приложение на две основные части:
- Оболочка (Shell) — минимальный HTML, CSS и JavaScript, необходимый для работы пользовательского интерфейса
- Контент — динамические данные, которые загружаются при необходимости
Такой подход позволяет приложению быстро загружаться и работать даже при плохом соединении. Оболочка кешируется при первой загрузке и повторно используется при последующих посещениях, в то время как контент может обновляться асинхронно.
Реализация отзывчивого дизайна и быстродействия PWA
Отзывчивый дизайн и быстродействие — два столпа успешного PWA. Пользователи ожидают, что приложения будут работать плавно на любом устройстве и при любом качестве сети. Исследования показывают, что 53% мобильных пользователей покидают сайт, если он загружается более 3 секунд. Оптимизация этих параметров напрямую влияет на конверсию. 🚀
Для создания по-настоящему отзывчивого PWA следуйте этим принципам:
- Используйте гибкие сетки с относительными единицами (%, em, rem)
- Применяйте медиа-запросы для адаптации контента к разным экранам
- Оптимизируйте изображения и используйте современные форматы (WebP, AVIF)
- Внедряйте стратегию "Mobile First" при разработке стилей
Пример базовой структуры CSS для отзывчивого дизайна:
/* Base styles (mobile first) */
.container {
width: 100%;
padding: 1rem;
}
/* Tablet styles */
@media (min-width: 768px) {
.container {
width: 750px;
margin: 0 auto;
}
}
/* Desktop styles */
@media (min-width: 1024px) {
.container {
width: 970px;
}
}
/* Large desktop styles */
@media (min-width: 1200px) {
.container {
width: 1170px;
}
}
Для оптимизации быстродействия PWA критически важно следовать этим практикам:
Минимизация критического пути рендеринга
- Размещайте критические CSS в теге
<head> - Откладывайте загрузку некритических ресурсов
- Используйте асинхронную загрузку скриптов (async/defer)
- Размещайте критические CSS в теге
Оптимизация изображений
- Используйте правильные размеры и форматы
- Применяйте ленивую загрузку (lazy loading)
- Внедряйте тег
<picture>для адаптивных изображений
Эффективное кеширование
- Настраивайте правильные HTTP-заголовки
- Используйте хеши в именах файлов для версионирования
- Применяйте стратегии кеширования Service Worker
Максим Ветров, ведущий frontend-разработчик
Работая над PWA для крупного интернет-магазина электроники, я столкнулся с проблемой: сайт загружался за 7-8 секунд на мобильных устройствах, показатель отказов достигал 65%, а конверсия падала.
Первым шагом стал аудит производительности через Lighthouse. Результаты были удручающими: 35 баллов из 100 по быстродействию. Основные проблемы: неоптимизированные изображения, блокирующие CSS/JS и отсутствие кеширования.
Я реализовал стратегию Application Shell — создал минимальный интерфейс, который кешировался при первом посещении, а затем загружался мгновенно. Для изображений внедрил ленивую загрузку и предзагрузку критических ресурсов через
<link rel="preload">.Радикальное улучшение принесла оптимизация CSS. Анализатор показал, что мы используем только 23% Bootstrap. Я вырезал все неиспользуемые стили, уменьшив CSS-файлы на 78%. Критические стили были внедрены в
<head>, остальные подгружались асинхронно.После всех оптимизаций время загрузки сократилось до 2,1 секунды, показатель отказов снизился до 38%, а конверсия выросла на 29%. Lighthouse теперь показывает 87 баллов по производительности. Но самый значимый результат — увеличение выручки магазина на 18% за первые три месяца после внедрения PWA.
Для оценки и улучшения производительности PWA используйте следующие метрики:
- First Contentful Paint (FCP) — время до первого отображения контента
- Largest Contentful Paint (LCP) — время отображения основного контента
- First Input Delay (FID) — задержка первого взаимодействия пользователя
- Cumulative Layout Shift (CLS) — суммарное смещение макета
Инструменты для измерения этих метрик:
- Google Lighthouse — комплексный инструмент для анализа PWA
- Chrome DevTools — панель Performance
- PageSpeed Insights — онлайн-анализатор производительности
- Web Vitals расширение для Chrome — для быстрых измерений
Service Workers: ключ к автономной работе приложения
Service Workers — это JavaScript-файлы, которые работают в фоновом режиме, отдельно от веб-страницы, предоставляя возможности, которые раньше были доступны только нативным приложениям. Они являются основой офлайн-функциональности PWA, позволяют реализовать кеширование ресурсов и поддерживать пуш-уведомления. 🛠️
Жизненный цикл Service Worker проходит через несколько фаз:
- Регистрация — сообщает браузеру о существовании Service Worker
- Установка — происходит, когда Service Worker загружается впервые или обновляется
- Активация — когда Service Worker становится действующим
- Ожидание — после установки, но перед активацией (если уже есть активный Service Worker)
- Завершение — когда Service Worker перестает быть активным
Начнем с базовой регистрации Service Worker. Добавьте следующий код в ваш основной JavaScript файл (например, app.js):
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/js/service-worker.js')
.then(function(registration) {
console.log('ServiceWorker успешно зарегистрирован со сферой действия: ', registration.scope);
})
.catch(function(error) {
console.log('Регистрация ServiceWorker провалена: ', error);
});
});
}
Теперь создадим базовый Service Worker (service-worker.js):
const CACHE_NAME = 'pwa-cache-v1';
const urlsToCache = [
'/',
'/index.html',
'/css/styles.css',
'/js/app.js',
'/images/icons/icon-192x192.png',
// Добавьте все ресурсы, которые хотите кешировать
];
// Установка Service Worker и кеширование ресурсов
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Кеш открыт');
return cache.addAll(urlsToCache);
})
);
});
// Активация Service Worker и очистка устаревших кешей
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
// Перехват сетевых запросов
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// Возврат кешированного ресурса, если он есть
if (response) {
return response;
}
// Клонирование запроса, так как он может быть использован только один раз
const fetchRequest = event.request.clone();
return fetch(fetchRequest).then(response => {
// Проверка валидности ответа
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// Клонирование ответа, так как он может быть использован только один раз
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});
Существует несколько стратегий кеширования, каждая из которых подходит для разных типов ресурсов:
| Стратегия | Описание | Применение |
|---|---|---|
| Cache First | Сначала проверяет кеш, затем сеть | Статические ресурсы (CSS, JS, изображения) |
| Network First | Сначала пытается получить из сети, потом из кеша | Динамический контент, API-запросы |
| Stale While Revalidate | Возвращает кешированный ответ, затем обновляет кеш | Контент, который может обновляться, но не критично быстро |
| Cache Only | Только из кеша, никогда из сети | Офлайн-приложения, критические ресурсы оболочки |
| Network Only | Только из сети, никогда из кеша | Данные, которые всегда должны быть свежими |
Пример реализации стратегии "Stale While Revalidate":
self.addEventListener('fetch', event => {
if (event.request.url.includes('/api/')) {
event.respondWith(
caches.open(CACHE_NAME).then(cache => {
return fetch(event.request).then(networkResponse => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
}).catch(() => {
return cache.match(event.request);
});
})
);
} else {
// Для остальных ресурсов используем Cache First
event.respondWith(
caches.match(event.request)
.then(response => {
return response || fetch(event.request);
})
);
}
});
Для улучшения пользовательского опыта можно реализовать фоновую синхронизацию с помощью Service Worker:
// В вашем app.js
function saveDataLocally(data) {
// Сохранение данных в IndexedDB
// ...
// Запрос на фоновую синхронизацию
if ('serviceWorker' in navigator && 'SyncManager' in window) {
navigator.serviceWorker.ready.then(registration => {
return registration.sync.register('sync-data');
});
} else {
// Немедленная синхронизация, если API не поддерживается
sendDataToServer(data);
}
}
// В вашем service-worker.js
self.addEventListener('sync', event => {
if (event.tag === 'sync-data') {
event.waitUntil(
// Получение данных из IndexedDB и отправка на сервер
// ...
);
}
});
Для реализации пуш-уведомлений:
// В вашем app.js
function subscribeToPushNotifications() {
navigator.serviceWorker.ready.then(registration => {
return registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('ваш_публичный_ключ')
});
}).then(subscription => {
// Отправьте подписку на ваш сервер
return fetch('/api/subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(subscription)
});
}).catch(error => {
console.error('Ошибка подписки на пуш-уведомления:', error);
});
}
// В вашем service-worker.js
self.addEventListener('push', event => {
const data = event.data.json();
const options = {
body: data.body,
icon: '/images/icons/icon-192x192.png',
badge: '/images/icons/badge-72x72.png',
vibrate: [100, 50, 100],
data: {
url: data.url
}
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
});
Развёртывание и тестирование PWA для разных устройств
После создания PWA необходимо тщательно протестировать и правильно развернуть приложение. Развертывание и тестирование PWA отличается от обычных веб-приложений, поскольку необходимо учитывать особенности работы в офлайн-режиме, установку на устройства и взаимодействие с различными платформами. 📱💻
Перед развертыванием убедитесь, что ваше PWA соответствует следующим критериям:
- Работает по HTTPS (обязательно для Service Worker)
- Имеет корректный Web App Manifest
- Реализует адаптивный дизайн для всех размеров экрана
- Функционирует в офлайн-режиме
- Загружается быстро даже на медленных соединениях
Чек-лист для развертывания PWA:
Настройка HTTPS
- Получите SSL-сертификат (Let's Encrypt предоставляет бесплатные сертификаты)
- Настройте перенаправление с HTTP на HTTPS
- Проверьте наличие mixed content (смешанного контента)
Оптимизация для поисковых систем
- Добавьте мета-теги для правильной индексации
- Реализуйте микроданные schema.org
- Создайте карту сайта (sitemap.xml)
Настройка серверной части
- Установите правильные HTTP-заголовки кеширования
- Настройте сжатие ресурсов (Gzip, Brotli)
- Реализуйте серверную логику для пуш-уведомлений
Инструменты для тестирования PWA:
- Lighthouse — встроенный в Chrome DevTools инструмент для комплексной оценки PWA
- Chrome DevTools — вкладка Application для тестирования Service Worker, манифеста и хранилища
- PWA Builder — проверяет соответствие требованиям PWA и помогает генерировать необходимые файлы
- BrowserStack — для тестирования на разных устройствах и платформах
- Puppeteer — для автоматизированного тестирования
При тестировании обратите внимание на следующие аспекты PWA:
| Аспект | Что проверять | Инструменты |
|---|---|---|
| Установка | Появление баннера установки, процесс установки на разных платформах | Chrome для Android, Safari для iOS |
| Офлайн-работа | Поведение при отключении интернета, доступ к кешированному контенту | DevTools (Network tab → Offline) |
| Производительность | Время загрузки, отзывчивость интерфейса, потребление ресурсов | Lighthouse, WebPageTest |
| Доступность | Контрастность, навигация с клавиатуры, совместимость со скринридерами | Axe DevTools, Chrome Accessibility Audit |
| Совместимость | Работа на различных устройствах, браузерах и операционных системах | BrowserStack, CrossBrowserTesting |
Несколько практических советов по тестированию PWA:
- Тестируйте на реальных устройствах, а не только в эмуляторах. Особенно важно проверить работу на iOS, где поддержка PWA имеет ограничения.
- Симулируйте различные сетевые условия (3G, 4G, офлайн) для проверки стратегий кеширования.
- Проверьте процесс обновления PWA — что происходит, когда вы обновляете Service Worker или манифест.
- Тестируйте различные сценарии использования, включая переходы между онлайн и офлайн режимами.
- Убедитесь, что пользовательские данные корректно сохраняются между сессиями (IndexedDB, localStorage).
После успешного тестирования и развертывания не забудьте о мониторинге и аналитике:
- Настройте отслеживание установок PWA через Google Analytics
- Мониторьте производительность с помощью Real User Monitoring (RUM)
- Отслеживайте ошибки Service Worker через систему логирования
- Собирайте отзывы пользователей о работе приложения
Популярные хостинг-платформы для PWA:
- Firebase Hosting — интегрируется с другими сервисами Firebase
- Netlify — автоматизирует деплой из Git-репозитория
- Vercel — оптимизирован для фронтенд-фреймворков
- GitHub Pages — бесплатный хостинг для статических сайтов
- AWS Amplify — полный стек для веб и мобильных приложений
PWA — это не просто модный термин, а технология, которая стирает границы между веб и нативными приложениями, предоставляя пользователям лучший опыт независимо от устройства или качества соединения. Следуя шагам из этого руководства, вы создадите приложение, которое загружается молниеносно, работает офлайн и выглядит как нативное. Начните с малого — внедрите базовый манифест и простой Service Worker, постепенно расширяя функциональность. Помните: каждая оптимизация имеет значение. Даже простое PWA может значительно повысить вовлеченность пользователей и конверсию по сравнению с обычным сайтом. Действуйте — будущее веба уже здесь.
Станислав Плотников
фронтенд-разработчик