Что такое PWA, как они работают и как создать PWA
Создание сайта

Что такое PWA, как они работают и как создать PWA

PWA (Progressive Web App, прогрессивное веб-приложение) — это сайт, который использует стандартные веб-технологии для установки на устройство, запуска в отдельном окне, офлайн-работы и других возможностей приложения.

Елена Кравцова
Елена Кравцова
Редактор и автор статей13 мин

PWA (Progressive Web App, прогрессивное веб-приложение) — это сайт, который использует стандартные веб-технологии для установки на устройство, запуска в отдельном окне, офлайн-работы и других возможностей приложения. Для управляемой установки обычно нужны HTTPS и файл manifest, а для офлайн-режима — service worker с продуманным кешированием. Ни manifest, ни кнопка «Установить» сами по себе не сделают сайт полезным без интернета: доступные офлайн-экраны, данные, обновления и восстановление после очистки хранилища нужно проектировать отдельно.

Что такое PWA и когда она нужна

PWA остаётся веб-приложением: его интерфейс написан на HTML, CSS и JavaScript, страницы доступны по URL, а новая версия размещается на сервере. Совместимый браузер может добавить приложение на домашний экран, в меню «Пуск», Dock или панель задач. После запуска оно выглядит как отдельная программа, если выбран режим standalone.

Слово Progressive означает постепенное улучшение. Базовый сайт должен продолжать выполнять основную задачу в браузере. Установка, офлайн-кеш, push-уведомления и интеграция с операционной системой подключаются там, где их поддерживает устройство.

PWA особенно полезна в следующих случаях:

  • у сервиса уже есть адаптивная веб-версия, и команда хочет развивать одну кодовую базу;
  • пользователи часто возвращаются в личный кабинет, каталог, редактор, программу лояльности или внутреннюю систему;
  • часть интерфейса и ранее открытые данные должны загружаться при нестабильной сети;
  • продукту важна установка прямо с сайта без обязательного перехода в магазин приложений;
  • нужны web push-уведомления, но команда готова учитывать ограничения каждой платформы.

Нативное или гибридное приложение может лучше подойти, если продукт зависит от непрерывной фоновой работы, специфичных системных API, сложной обработки медиа или обязательного присутствия в магазинах. PWA и нативный клиент могут существовать одновременно: веб-приложение закрывает широкий сценарий, а платформенный клиент — функции, которых нет в нужном браузере.

Как устроена PWA

У PWA нет единственного обязательного программного каркаса. React, Vue, Angular и серверный рендеринг допустимы, но не определяют саму технологию. Практический минимум складывается из нескольких независимых частей.

Web App Manifest

Manifest — JSON-файл с метаданными установленного приложения. В нём задают:

  • полное и короткое название;
  • стабильный идентификатор id;
  • стартовый URL и область приложения scope;
  • режим отображения, например standalone;
  • цвета интерфейса;
  • иконки обычного и maskable-типа.

Manifest помогает браузеру показать корректное имя и иконку, открыть нужный адрес и отделить окно приложения от вкладки. Он не управляет сетевыми запросами и не создаёт офлайн-режим.

Service worker и HTTPS

Service worker — JavaScript-файл, который браузер запускает отдельно от страницы. Он может перехватывать запросы в своей области действия, отвечать данными из Cache API, получать push-события и выполнять поддерживаемые фоновые задачи. Прямого доступа к DOM страницы у него нет.

Service worker работает в безопасном контексте. На рабочем сайте нужен HTTPS; исключение для разработки — localhost и 127.0.0.1. Положение файла влияет на область действия: /sw.js по умолчанию контролирует корень сайта, а /assets/sw.js — только /assets/ и вложенные адреса, если сервер не расширил scope специальным заголовком.

Жизненный цикл состоит из установки, ожидания и активации. При обновлении новая версия worker обычно устанавливается рядом с действующей, затем ждёт, пока старая перестанет контролировать открытые вкладки. Благодаря этому интерфейс и worker одной версии не смешиваются без решения разработчика.

Cache API, IndexedDB и сервер

Cache API хранит пары Request/Response: HTML, стили, скрипты, изображения и ответы GET-запросов. IndexedDB подходит для структурированных данных — черновиков, очереди несинхронизированных действий и ранее полученных записей. localStorage внутри service worker недоступен.

Браузерное хранилище имеет квоты. Его может очистить пользователь, а при нехватке места — браузер. Поэтому заказ, документ или другое критическое изменение нельзя считать сохранённым, пока сервер не подтвердил запись. Локальная копия ускоряет работу и помогает пережить обрыв сети, но серверная синхронизация остаётся частью архитектуры.

Что реально поддерживают браузеры в 2026 году

У PWA нет единого статуса Baseline, потому что это сочетание функций. Service Worker, Cache API и Push API относятся к Baseline Widely available, но правила установки, разрешения и системная интеграция различаются. beforeinstallprompt, Background Sync и Periodic Background Sync имеют Limited availability и не подходят как единственный путь к важной функции.

Актуальные условия installability на MDN также разделяют установку и офлайн-режим: service worker широко используется для offline, но не является универсальным условием добавления сайта как приложения.

Платформа Как устанавливают Что учесть
Android Через команду Install или «Добавить на главный экран» в браузере Результатом может быть WebAPK либо ярлык — это зависит от браузера, устройства и соответствия критериям. Проверяйте именно целевые браузеры.
iOS и iPadOS 26 Share → Add to Home Screen; параметр Open as Web App включён по умолчанию Safari 26 позволяет добавить любой сайт как web app. Manifest по-прежнему нужен для контролируемых иконок, имени и запуска. Автоматического install prompt на iOS нет.
Windows, Linux, macOS в Chromium Значок установки в адресной строке или команда в меню браузера Установленная PWA получает отдельное окно и системный ярлык. Доступ к дополнительным API зависит от браузера и ОС.
macOS в Safari 17+ File → Add to Dock Safari может добавить в Dock сайт без manifest; manifest всё равно улучшает представление приложения.
Firefox на компьютере Штатной установки PWA по manifest нет Сайт продолжит работать в браузере, а поддерживаемые service worker-функции можно использовать без установки. Firefox на Android поддерживает добавление веб-приложений.

Проверка только по названию браузера ненадёжна. Используйте feature detection, например "serviceWorker" in navigator, и сохраняйте обычный сетевой путь для неподдерживаемых функций.

Какой способ создания выбрать

Способ Когда выбирать Что он экономит Что остаётся сделать
Вручную: manifest и собственный service worker Небольшой сайт, обучение, особые правила offline Нет лишней зависимости, полный контроль Самостоятельно реализовать стратегии кеша, обновления, ошибки и тест-матрицу
Workbox или PWA-плагин фреймворка/сборщика Проект уже собирает ассеты с хешами, нужен precache и типовые стратегии Генерацию списка файлов, маршрутизацию кеша и часть lifecycle-кода Настроить API, пользовательские данные, обновление интерфейса и платформенные проверки
CMS-плагин или PWABuilder Нужна базовая установка существующего сайта либо заготовка пакета для магазина Создание manifest, иконок, простого worker и упаковки Проверить качество исходного сайта, scope, offline, авторизацию, push и требования магазина

В Workbox есть два характерных пути. generateSW создаёт готовый worker для простого precache и настраиваемого runtime cache. injectManifest добавляет список сборочных файлов в собственный worker и оставляет разработчику сложную маршрутизацию, push и другую логику.

Плагин CMS заметно сокращает стартовую работу для блога или каталога. Он не знает, можно ли показывать устаревшую цену, как повторно отправить офлайн-заказ и что делать при конфликте черновиков. Такие правила задаёт владелец продукта.

PWABuilder полезен для аудита исходного сайта, генерации основы и подготовки store-пакета. Публикация через Trusted Web Activity, WebView-обёртку или другой контейнер является отдельным проектом: магазин проверяет пакет и соблюдение своих правил, а не только наличие manifest.

Как создать PWA вручную

Ниже — минимальный вариант для обычного адаптивного сайта. Он показывает архитектуру и даёт рабочий офлайн-fallback. Перед внедрением решите, какие URL можно кешировать и какие данные должны всегда приходить с сервера.

Подготовьте сайт и иконки

Проверьте, что интерфейс удобен с сенсорным экраном и клавиатурой, каждая важная страница открывается по собственному URL, а рабочий домен использует HTTPS без смешанного HTTP-контента.

Подготовьте PNG-иконки 192×192 и 512×512 пикселей. Для Android полезна отдельная maskable-иконка: её важная часть должна помещаться в безопасную центральную область, чтобы система могла обрезать изображение под круг или другую форму.

Создайте manifest

Сохраните файл /manifest.webmanifest:

{
  "name": "Мой сервис",
  "short_name": "Сервис",
  "id": "/",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#0b57d0",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512-maskable.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ]
}

Значения start_url и scope должны попадать в доступную область сайта. Стабильный id помогает браузеру узнавать приложение после изменения стартового URL.

Подключите manifest и зарегистрируйте worker

Добавьте в <head> каждой точки входа:

<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#0b57d0">
<link rel="apple-touch-icon" href="/icons/icon-192.png">

Зарегистрируйте worker в клиентском JavaScript:

if ("serviceWorker" in navigator) {
  window.addEventListener("load", async () => {
    try {
      const registration = await navigator.serviceWorker.register("/sw.js", {
        scope: "/"
      });
      console.log("Service worker scope:", registration.scope);
    } catch (error) {
      console.error("Service worker registration failed:", error);
    }
  });
}

Проверка поддержки сохраняет работу сайта в браузере без Service Worker API. Абсолютный путь /sw.js помогает охватить весь сайт.

Добавьте минимальный service worker

Создайте /sw.js:

const CACHE_PREFIX = "my-service-";
const CACHE_NAME = `${CACHE_PREFIX}shell-v1`;
const PRECACHE = [
  "/",
  "/offline.html",
  "/styles.css",
  "/app.js",
  "/icons/icon-192.png"
];
const STATIC_TYPES = new Set(["style", "script", "image", "font"]);

self.addEventListener("install", (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE))
  );
});

self.addEventListener("activate", (event) => {
  event.waitUntil(
    caches.keys().then((names) =>
      Promise.all(
        names
          .filter((name) =>
            name.startsWith(CACHE_PREFIX) && name !== CACHE_NAME
          )
          .map((name) => caches.delete(name))
      )
    )
  );
});

async function networkFirst(request) {
  try {
    const response = await fetch(request);
    if (response.ok) {
      const cache = await caches.open(CACHE_NAME);
      await cache.put(request, response.clone());
    }
    return response;
  } catch {
    return (await caches.match(request)) || caches.match("/offline.html");
  }
}

async function cacheFirst(request) {
  const cached = await caches.match(request);
  if (cached) return cached;

  const response = await fetch(request);
  if (response.ok) {
    const cache = await caches.open(CACHE_NAME);
    await cache.put(request, response.clone());
  }
  return response;
}

self.addEventListener("fetch", (event) => {
  const request = event.request;
  if (request.method !== "GET") return;

  if (request.mode === "navigate") {
    event.respondWith(networkFirst(request));
    return;
  }

  const sameOrigin = new URL(request.url).origin === self.location.origin;
  if (sameOrigin && STATIC_TYPES.has(request.destination)) {
    event.respondWith(cacheFirst(request));
  }
});

Этот пример рассчитан на публичные страницы: он сначала запрашивает навигацию по сети, сохраняет успешный ответ и при сбое возвращает ранее открытую страницу либо /offline.html. Статические ресурсы своего домена обслуживаются из кеша. POST-запросы и API-ответы намеренно не кешируются. В приложении с авторизацией ограничьте кешируемые маршруты явным списком и не сохраняйте персональный HTML общим правилом.

Создайте простую самодостаточную /offline.html. В ней должны быть понятное сообщение, кнопка повторной попытки и только те стили или иконки, которые входят в precache. Если хотя бы один URL из PRECACHE отвечает ошибкой, cache.addAll() отклонится и новая версия worker не установится.

Настройте ответы сервера

Проверьте четыре условия:

  • /manifest.webmanifest, /sw.js и все иконки отвечают без перенаправлений и ошибок;
  • сервер отдаёт manifest как application/manifest+json или совместимый JSON-тип, а worker — как JavaScript;
  • sw.js не получает многомесячный immutable-кеш; практичный вариант — Cache-Control: no-cache, чтобы браузер мог быстро сверять версию;
  • файлы с хешем в имени можно кешировать надолго, а HTML и данные — по правилам их актуальности.

При изменении app shell увеличьте версию my-service-shell-v1. Обработчик activate удалит только кеши этого приложения прошлых версий после активации нового worker.

Как сделать офлайн-режим полезным

«Работает офлайн» должно обозначать конкретный сценарий. Например: приложение запускается, показывает ранее открытые статьи, сохраняет черновик и сообщает, что отправит его после возвращения сети. Полный каталог, свежие остатки и оплата могут оставаться сетевыми функциями.

Выбирайте стратегию по типу ресурса:

  • Precache — оболочка интерфейса, offline-страница, базовые стили и код текущей версии.
  • Cache first — файлы с хешированными именами, шрифты и неизменяемые изображения.
  • Stale while revalidate — некритичные аватары и справочные данные, где быстрый старый ответ допустим, а обновление можно скачать в фоне.
  • Network first — ранее открытые документы или лента, если важна свежесть, но при сбое допустима копия с отметкой времени.
  • Network only — авторизация, оплата, остатки, персональные права и операции, для которых устаревший ответ опаснее отсутствия ответа.

Для офлайн-форм сохраняйте черновик и очередь в IndexedDB. Каждой операции назначайте устойчивый идентификатор, а сервер делайте идемпотентным: повторная отправка после обрыва не должна создавать второй заказ. Если Background Sync недоступен, отправляйте очередь при следующем открытии приложения или событии online и явно показывайте статус пользователю.

Не кешируйте по общему правилу все GET-ответы. В кеше могут оказаться персональные данные, ответы с ошибками или информация одного аккаунта после выхода из системы. Для приватных данных нужны очистка при logout, ограничение времени жизни и проверка пользователя на сервере.

Как работают установка, push и обновления

Установка

В Chromium можно показать собственную кнопку установки через событие beforeinstallprompt. Кнопка должна быть скрыта, пока событие не получено. Этот API не является Baseline и не работает на iOS, поэтому рядом нужен обычный путь через меню браузера или короткая инструкция Share → Add to Home Screen.

Установку предлагайте после того, как человек понял пользу приложения: например, после сохранения первого документа или повторного визита. Немедленный баннер на первой странице конкурирует с основным действием и не объясняет, зачем добавлять сайт на устройство.

Push-уведомления

Manifest не включает push автоматически. Для классического Web Push нужны service worker, подписка браузера, сервер отправки и обработчик клика по уведомлению. Разрешение запрашивайте после явного действия — кнопки «Включить уведомления» с описанием их типа и частоты.

На iOS и iPadOS Web Push поддерживается с версии 16.4 для веб-приложений, добавленных на Home Screen. В обычной вкладке тот же сценарий рассчитывать на push не должен. На каждой платформе проверяйте PushManager, Notifications API и фактическое состояние разрешения.

Обновления

Браузер периодически сравнивает файл worker с серверной версией. Если код изменился, он устанавливает новую версию в фоне. Действующий worker продолжает обслуживать открытые вкладки, а новый может оставаться в waiting до их закрытия.

Для небольшого сайта достаточно сообщить «Доступна новая версия» и предложить перезагрузку после активации. В сложном приложении согласуйте версии HTML, API и кеша. Безусловный skipWaiting() ускоряет активацию, но новый worker способен начать обслуживать старую открытую страницу. Используйте его только тогда, когда версии совместимы или интерфейс контролируемо перезагружается.

Изменение иконки или названия в manifest может применяться не сразу и зависит от платформы. Иногда пользователю потребуется переустановка. Не связывайте выпуск критического исправления только с обновлением метаданных установленного ярлыка.

Как проверить результат

Проверка начинается с чистого состояния. Старый worker и кеш часто скрывают ошибку, поэтому используйте новый профиль браузера либо очистите данные сайта перед первым проходом.

Manifest и область действия

В Chromium или Edge откройте DevTools → Application:

  • в Manifest нет ошибок, видны правильные name, start URL, display и иконки;
  • maskable-иконка сохраняет важную часть в безопасной области;
  • service worker имеет ожидаемый статус и scope /;
  • в Cache Storage находятся только запланированные ресурсы.

Актуальный Lighthouse больше не содержит отдельной категории PWA. Он полезен для производительности, доступности и общих проверок качества, но не заменяет Application и реальные устройства.

Офлайн и навигация

Сначала откройте приложение онлайн, дождитесь активации worker и перезагрузите страницу. Затем включите Offline в DevTools или отключите сеть и проверьте:

  • запуск с установленной иконки;
  • перезагрузку главной и ранее открытой вложенной страницы;
  • прямой переход по deep link;
  • отображение offline-страницы для неизвестного маршрута;
  • поведение изображений, шрифтов и API;
  • понятный статус несохранённой формы и повторную отправку после возврата сети.

Обновление

Оставьте старую вкладку открытой, опубликуйте тестовую версию worker с новым именем кеша и откройте вторую вкладку. Убедитесь, что новая версия появляется в waiting, старый интерфейс продолжает работать, а после выбранного действия или закрытия вкладок активируется согласованный набор файлов. Отдельно проверьте, что старые кеши удалились и обновление не требует двух случайных перезагрузок.

Установка и восстановление

Минимальная матрица — Android в основном браузере аудитории, iPhone/iPad через Home Screen и компьютерный Chromium. Если важны macOS или Firefox, добавьте Safari Add to Dock и обычную браузерную версию Firefox.

После успешного сценария вручную очистите данные сайта. Приложение должно корректно войти заново, скачать серверные записи и объяснить потерю несинхронизированного локального черновика. Такая проверка не воспроизводит все алгоритмы eviction, но подтверждает, что локальное хранилище не стало единственной копией данных.

Push проверяйте только если он реализован: разрешение после клика, отказ пользователя, получение в фоне, переход по уведомлению на правильный URL и повторную подписку после очистки данных. На iOS выполняйте тест из установленной Home Screen web app.

Типичные ошибки

  • Установка принята за готовую PWA. Иконка и отдельное окно не подтверждают offline, обновления и восстановление данных.
  • Worker лежит слишком глубоко. Файл в /assets/ не контролирует маршруты корня с областью действия по умолчанию.
  • В precache указан несуществующий файл. Один ответ 404 срывает cache.addAll() и установку новой версии worker.
  • Одна стратегия применена ко всем запросам. Cache first показывает старые цены и права, а network only лишает приложение офлайн-оболочки.
  • Старые кеши не версионируются. Пользователь получает несовместимые HTML и JavaScript или долго видит прошлый релиз.
  • skipWaiting() включён без сценария обновления. Новый worker начинает отвечать старой открытой странице.
  • Push запрашивается на первом экране. Пользователь не понимает ценность, запрещает уведомления, а повторный запрос блокируется настройками браузера.
  • Локальные данные считаются постоянными. Очистка сайта, приватный режим, квота или нехватка места удаляют кеш и IndexedDB.
  • Проверяется только localhost. На рабочем домене проявляются ошибки HTTPS, MIME-типа, редиректов, заголовков кеша и scope.
  • Тест ограничен Lighthouse. Нужны Application, чистое состояние и реальные сценарии на целевых устройствах.

Автор статьи

Елена Кравцова — Редактор и автор статей
Елена Кравцова

Редактор и автор статей

Пишет экспертные материалы о цифровом маркетинге и автоматизации. Журналист с опытом в деловых медиа, отвечает за качество и достоверность публикаций.

Вопросы и ответы

Почти любой современный адаптивный сайт можно сделать устанавливаемым, добавив manifest и корректную серверную настройку. Полезная PWA потребует больше работы: нужно определить офлайн-сценарии, кеш, обновления, авторизацию и восстановление данных. Генератор не исправит неудобный мобильный интерфейс и не создаст бизнес-логику автоматически.

Не во всех браузерах. Safari 26 на iOS/iPadOS и Safari на macOS позволяют добавить любой сайт как web app, а некоторые Chromium-сценарии также устанавливают сайт без прежнего набора критериев. Service worker нужен для управляемого офлайн-режима, классического Web Push и других фоновых возможностей.

Да. В iOS/iPadOS 26 пользователь выбирает Share → Add to Home Screen, после чего сайт по умолчанию открывается как web app. Автоматического диалога установки, управляемого через beforeinstallprompt, нет. Web Push работает для Home Screen web apps начиная с iOS/iPadOS 16.4 и требует разрешения после действия пользователя.

Может, если все нужные ресурсы и данные заранее сохранены, а операции не требуют сервера. Для магазина, банка или совместного редактора разумнее частичный offline: оболочка, ранее открытые данные и черновики доступны, а оплата, свежие остатки и подтверждение изменений ждут сеть.

Да, через платформенные пакеты и обёртки: например, Trusted Web Activity на Android или создаваемый инструментом пакет для Microsoft Store. Для App Store применяется отдельная оболочка и правила Apple. В каждом случае нужно пройти требования и проверку магазина; веб-установка с сайта от этого не зависит.

Наличие manifest и service worker само по себе не повышает позиции. Поисковику по-прежнему нужны доступные URL, индексируемый контент, корректные ответы сервера, скорость и внутренняя перелинковка. Service worker не должен маскировать серверные ошибки одинаковым ответом 200 для всех адресов.

Код и данные загружаются с сайта, а браузер проверяет обновления service worker. Новая версия может ждать закрытия вкладок. Приложению нужен понятный сценарий: фоновая установка, сообщение о готовом обновлении и безопасная перезагрузка. Метаданные manifest, включая иконку, обновляются по правилам конкретной платформы и иногда требуют переустановки.

Единого гарантированного объёма нет. Квота зависит от браузера, устройства, свободного места и активности пользователя. navigator.storage.estimate() показывает оценку, а navigator.storage.persist() может запросить постоянный режим, но результат не гарантирован. Критические данные синхронизируйте с сервером и обрабатывайте QuotaExceededError.

Смотрите также

Поделиться

Комментарии(0)

Оставьте комментарий

Войдите, чтобы присоединиться к обсуждению