Створення PWA у 2026: як зробити JavaScript-застосунки доступними офлайн
Користувачі очікують, що застосунки працюватимуть бездоганно незалежно від стану мережі. JavaScript-застосунки з офлайн-підтримкою на базі Progressive Web Apps (PWA) поєднують доступність вебу з можливостями нативних програм, при цьому коштуючи на 33–50% менше у розробці.
У цій статті ми розглянемо, як створити PWA у 2026 році, використовуючи service worker для офлайн-функціоналу, стратегії кешування даних (зокрема, cache-first стратегію) та реалізацію у React-застосунках. Ви дізнаєтеся, як зробити ваші вебзастосунки доступними без інтернету.
Основи PWA-архітектури для офлайн-застосунків
Progressive Web App — це вебзастосунок, який поєднує переваги звичайних вебсайтів та нативних мобільних програм. Термін виник у 2015 році завдяки дизайнеру Францес Берріман (Frances Berriman) та розробнику Google Chrome Алексу Расселу. На відміну від традиційних вебсайтів, PWA працюють на базі стандартних вебтехнологій HTML, CSS та JavaScript, водночас надаючи можливості, характерні для встановлених застосунків.
Що таке Progressive Web App
Офлайн-функціональність часто сприймають як зручність для пасажирів у потягах чи літаках, але це занадто вузький погляд. Набагато частіше ми стикаємося з нестабільним інтернетом: слабкий мобільний сигнал, перевантажена мережа в кав'ярні, кілька секунд без зв'язку, коли телефон перемикається між вишками стільникового оператора. За таких умов звичайний вебзастосунок не просто працює гірше — він відмовляє повністю: порожня сторінка, нескінченний спінер, втрачені дані з форми. Тому архітектура з підтримкою офлайн-режиму — це насправді архітектура надійності.
PWA демонструють високу швидкодію та здатність працювати офлайн незалежно від операційної системи. Вони ефективно поєднують переваги мобільних застосунків та вебсайтів, беручи найкраще з обох світів. Користувачі отримують можливість додати ярлик на головний екран, налаштувати push-сповіщення та працювати без підключення до мережі.
Три базові компоненти PWA
Архітектура PWA ґрунтується на трьох ключових компонентах:
- клієнтська частина (frontend), яка працює в браузері та відповідає за інтерфейс користувача і візуальне представлення;
- серверна частина (backend), що обробляє запити та надає дані клієнтській стороні;
- маніфест застосунку (manifest) — JSON-файл з метаданими програми.
Service Worker (сервісний воркер) виступає програмованим проксі-сервером, який розгортається на пристрої користувача. Це скрипт, що виконується незалежно від вебсторінки, не має доступу до DOM і може перехоплювати мережеві запити від сторінок під його контролем. Сервісний воркер забезпечує обробку запитів користувача, надсилання push-сповіщень, фонову синхронізацію та роботу в офлайн-режимі.
Архітектура оболонки застосунку (App Shell) включає базові елементи інтерфейсу, необхідні для роботи без підключення до інтернету. Оболонка не залежить від мережі. При повторному відвідуванні каркас з'являється на екрані ще до того, як будь-який запит залишить пристрій. Оскільки структура вже завантажена на момент отримання контенту, не виникає зміщення макета, що позитивно впливає на показник CLS.
ПРИЄДНУЙСЯ ДО НАШОЇ КОМАНДИ
Відмінності від звичайних вебсайтів
Звичайні вебсайти здебільшого виконують інформаційну функцію, тоді як вебзастосунки пропонують значно більше інтерактивних можливостей. Стандартний вебзастосунок можна відкрити лише через браузер на комп'ютері чи мобільному пристрої. PWA не потребують встановлення, оскільки основна частина функціоналу працює в хмарному середовищі.
Одна з ключових переваг прогресивних вебзастосунків — здатність коректно працювати навіть за нестабільного інтернет-з'єднання або повної його відсутності. Це забезпечується кешуванням даних вебсторінок, які відвідує користувач. Варто зазначити, що нова інформація з'явиться лише після відновлення підключення до мережі.
HTTPS як обов'язкова вимога
Використання протоколу безпеки HTTPS — обов'язкова вимога для прогресивних вебзастосунків. PWA мають передаватися через безпечне TLS-з'єднання (HTTPS). Це є суворою вимогою браузерів для активації Service Worker та доступу до сучасних Web API. HTTPS необхідний для роботи Service Worker та доступу до низки API браузера, зокрема API геолокації.
Безпечне походження передбачає відсутність активного змішаного контенту. Якщо розробник проігнорував цю рекомендацію та налаштував незахищене HTTP-з'єднання, браузер просто не відобразить дані на екрані при переході на сайт за цим протоколом. Це особливо критично для застосунків, пов'язаних з платежами, де безпека клієнтських даних має першочергове значення.
Service Worker: життєвий цикл та реєстрація
Життєвий цикл service worker складається з кількох послідовних етапів, які браузер виконує автоматично. Розуміння цього циклу дає змогу створювати надійні застосунки з офлайн-підтримкою та ефективним кешуванням даних.
Реєстрація Service Worker у JavaScript
Реєстрація — це перший крок життєвого циклу service worker. Спершу потрібно перевірити, чи підтримує браузер цю технологію, адже service worker працюють лише через HTTPS або localhost.
Типовий код реєстрації виглядає так:
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(() => console.log('Service worker зареєстровано!'))
.catch(() => console.error('Реєстрація не вдалася'));
});
}
Реєстрацію краще проводити після повного завантаження сторінки, щоб уникнути конкуренції за пропускну здатність, особливо коли service worker виконує попереднє кешування ресурсів. Метод register() приймає шлях до файлу воркера та повертає проміс. Браузер вважає service worker новим, якщо файл відрізняється від поточного хоча б на один байт.
Етап Installation та precaching ресурсів
Подія install спрацьовує одразу після успішної реєстрації та викликається лише один раз для кожного service worker. Це момент для кешування всіх необхідних ресурсів до того, як воркер зможе контролювати сторінки. Precaching (попереднє кешування) надає розробникам повний контроль над кешем, визначаючи, коли та як довго файл зберігається, а також дає змогу обслуговувати застосунок без звернення до мережі.
Workbox створює маніфест precache зі списком файлів та версійною інформацією, що слугує джерелом достовірних даних про ресурси для кешування. Якщо ресурс містить версію в назві файлу (хеш файлу), властивість revision може бути null. Для неверсійованих ресурсів ревізія генерується під час збірки.
Метод event.waitUntil() приймає проміс і очікує його виконання. Це гарантує, що service worker не встановиться, доки весь код всередині waitUntil() не завершиться успішно. Якщо проміс відхиляється, встановлення не відбувається, і браузер відкидає воркер.
Етап Activation та очищення старих кешів
Після успішного встановлення service worker активується та спрацьовує подія activate. Workbox перевіряє кешовані ресурси, яких більше немає у списку поточних URL, і видаляє їх з кешу. Це ключовий момент для очищення застарілих кешів, щоб не перевищувати квоти сховища.
Проміси, передані в waitUntil(), блокують інші події до свого завершення, тому операція очищення гарантовано закінчиться до першої події fetch для нового service worker. За замовчуванням новий service worker не контролює сторінку до наступної навігації або оновлення.
Обробка fetch подій
Подія fetch виникає щоразу, коли запитуються ресурси під контролем service worker. Метод event.respondWith() дає змогу перехопити HTTP-відповіді та змінити їх власним контентом. Саме тут реалізуються стратегії кешування для офлайн-доступу.
Виклик precacheAndRoute() створює маршрут зі стратегією cache-first: спочатку використовується кешована відповідь, і лише за її відсутності через несподівану помилку виконується мережевий запит. Важливо викликати precacheAndRoute() на початку файлу service worker, перед реєстрацією додаткових маршрутів через registerRoute().
skipWaiting() та clients.claim()
Метод skipWaiting() примушує service worker, що очікує, негайно стати активним. Його варто викликати всередині обробника події install, щоб будь-яка нова версія активувалася одразу. Водночас clients.claim() дає змогу активному service worker встановити себе контролером для всіх клієнтів у його області видимості.
Ці методи часто використовуються разом: skipWaiting() активує новий воркер негайно, а clients.claim() забезпечує контроль над усіма активними сторінками без їхнього перезавантаження. Це корисно для швидкого розгортання оновлень, хоча потрібно враховувати, що service worker контролюватиме сторінки, завантажені через мережу або інший воркер.
Стратегії кешування даних для офлайн доступу
Стратегія кешування визначає алгоритм, коли зберігати ресурс у кеші, коли віддавати закешовану відповідь і коли звертатися до мережі. Кожен підхід балансує між офлайн-роботою, швидкодією та актуальністю даних. Різні ресурси мають різні вимоги: базовий інтерфейс застосунку зазвичай відносно статичний, тоді як для списку товарів критично мати свіжі дані. Відповідно, JavaScript-застосунки з офлайн-підтримкою зазвичай використовують різні стратегії для різних типів ресурсів.
Cache-First стратегія для статичних ресурсів
Cache-First перевіряє наявність ресурсу в кеші і повертає його, якщо знаходить. Якщо ресурс відсутній, виконується мережевий запит, а отримана відповідь зберігається для подальшого використання. Precaching підходить для ресурсів, які застосунок гарантовано потребує, не змінюються у цій версії програми та мають бути завантажені якомога швидше. Це включає базовий інтерфейс користувача, CSS, JavaScript, зображення та шрифти з хеш-версіонуванням. Стратегія забезпечує миттєве завантаження без мережевих затримок, повну офлайн-підтримку та мінімальне споживання трафіку. Проте недоліком є обслуговування застарілого контенту до встановлення нової версії service worker.
Network-First для динамічного контенту
Network-First є протилежністю попереднього підходу: спочатку виконується спроба отримати ресурс з мережі. Якщо запит успішний, відповідь повертається, а кеш оновлюється. У разі невдачі використовується кешована версія. Ця стратегія корисна для запитів, де важливо отримати максимально свіжу відповідь, водночас кешований ресурс кращий за відсутність даних. Вона ідеально підходить для HTML-сторінок, API-запитів, панелей адміністратора, блогів, новинних сайтів та маркетплейсів. За можливості варто додати тайм-аут мережі: якщо мережа повільна, але не відсутня, ви все одно отримаєте швидку відповідь з кешу замість тривалого очікування.
Stale-While-Revalidate для балансу швидкості
Stale-While-Revalidate — найскладніша зі стратегій кешування, які ми розглянули. Вона ставить у пріоритет швидкий доступ до ресурсу, одночасно підтримуючи його актуальність у фоновому режимі. При першому запиті ресурс завантажується з мережі, зберігається в кеші та повертається. При наступних запитах ресурс спочатку віддається з кешу, а «у фоні» виконується мережевий запит для оновлення кешованого запису. Наступні запити отримають вже оновлену версію, завантажену з мережі. Цей підхід ідеально підходить для контенту, який не обов’язково має бути абсолютно свіжим: аватарів користувачів, рейтингів, рекомендацій, тегів. Натомість для HTML-навігації краще використовувати network-first із офлайн-фолбеком.
Вибір стратегії за типом ресурсу
Статичні ресурси (CSS, JavaScript, зображення, шрифти) найкраще обслуговувати за допомогою стратегії Cache-First з версіонуванням через ім’я кешу. HTML та API-запити потребують підходу Network-First, щоб дані завжди залишалися актуальними. Для контенту середньої важливості рекомендується використовувати Stale-While-Revalidate. Ніколи не кешуйте HTML за стратегією Cache-First: інакше користувачі назавжди залишаться на старій версії сайту і не побачать нових товарів, акцій чи виправлень.
Управління розміром кешу
Кеші мають обмежений обсяг зберігання, і браузер може видаляти дані застосунку при перевищенні ліміту. Без таких обмежень кеш міг би рости безкінечно. Підтримуйте порядок, встановлюючи обмеження на кількість записів (maxEntries) або час зберігання (maxAgeSeconds). Workbox елегантно вирішує це завдання за допомогою ExpirationPlugin, проте для базового service worker можна створити невелике сховище в IndexedDB із timestamp для кожного URL і періодично видаляти застарілі записи.
IndexedDB для структурованих даних
Кешування добре підходить для ресурсів, але для зберігання структурованих локальних даних варто використовувати IndexedDB. Це низькорівневе API для клієнтського зберігання великих обсягів структурованих даних, включно з файлами та BLOB-ами. IndexedDB застосовує індекси для високопродуктивного пошуку, підтримує асинхронні операції, транзакції для забезпечення цілісності даних і може зберігати гігабайти інформації. На відміну від localStorage, IndexedDB доступний у service worker під час фонової синхронізації та обробки push-сповіщень.
Реалізація офлайн-функціоналу в React додатках
Workbox спрощує створення JavaScript-застосунків з офлайн-підтримкою на React завдяки автоматичній генерації service worker та готовим стратегіям кешування. Спершу встановіть необхідні залежності для інтеграції з Vite:
npm install -D vite-plugin-pwa workbox-window
Пакет vite-plugin-pwa інтегрує Workbox з Vite і автоматично генерує сервісні воркери, тоді як workbox-window надає зручний інтерфейс для взаємодії з ними з боку вікна браузера.
Налаштування проєкту з Workbox
У файлі vite.config.ts додайте плагін VitePWA з налаштуваннями кешування. Властивість registerType встановіть у значення «autoUpdate» для автоматичного оновлення service worker при появі нової версії. У секції workbox.runtimeCaching визначте правила для різних типів ресурсів. Для API-запитів використовуйте обробник NetworkFirst з параметром networkTimeoutSeconds: 10, щоб після 10 секунд очікування мережі повернутися до кешу. Параметри expiration.maxEntries та maxAgeSeconds контролюють розмір кешу і термін зберігання даних.
Кешування API запитів
Для ефективного кешування даних використовуйте urlPattern для визначення маршрутів API. Наприклад, /api/.* перехопить усі запити до вашого бекенду. Стратегія NetworkFirst спочатку намагається отримати свіжі дані з мережі, а у разі невдачі використовує кешовану версію. Це забезпечує актуальність контенту за наявності з’єднання та доступність при його втраті. Встановіть cacheName як «api-cache» для окремого сегмента зберігання і maxAgeSeconds: 86400, щоб зберігати відповіді протягом 24 годин.
Офлайн-fallback сторінки
Створіть файл offline.html у папці public із повідомленням про відсутність з’єднання. У service worker додайте обробник fetch для навігаційних запитів. Коли користувач перебуває офлайн і запитує сторінку, якої немає в кеші, браузер за замовчуванням показує стандартну помилку. Натомість поверніть власну сторінку через caches.match('/offline.html') у разі помилки мережевого запиту. Перевірка event.request.mode === 'navigate' гарантує, що fallback спрацює лише для сторінок, а не для інших ресурсів.
Background Sync для відкладених запитів
Background Sync API вирішує проблему втрати даних під час офлайн-відправки форм. Зареєструйте синхронізацію через registration.sync.register('my-tag-name') з унікальним тегом. Коли з’єднання відновиться, у service worker спрацює подія sync. Обробник перевіряє тег і виконує відкладену операцію, передаючи проміс у event.waitUntil(). Зберігайте дані форм в IndexedDB під час офлайн-відправки, щоб service worker міг отримати їх для синхронізації.
Web App Manifest та встановлення PWA
Файл маніфесту вебзастосунку містить усю інформацію про JavaScript-застосунки з офлайн-підтримкою у форматі JSON. Цей документ надає браузеру метадані, необхідні для коректного встановлення PWA на пристрій користувача.
Створення manifest.json файлу
Маніфест зазвичай розміщують у кореневій папці застосунку і підключають через тег <link rel="manifest" href="manifest.json"> у секції <head> кожної HTML-сторінки. Специфікація W3C рекомендує розширення .webmanifest, проте браузери підтримують і .json. Мінімальні вимоги включають поле name та хоча б одну іконку з властивостями src, sizes та type. Рекомендовані поля: description, short_name та start_url.
Типова структура маніфесту виглядає так: поле name містить повну назву застосунку, short_name використовується на головному екрані за обмеженого простору, display визначає режим відображення (fullscreen, standalone, minimal-ui, browser), theme_color встановлює колір панелі інструментів, а background_color застосовується під час завантаження. Поле start_url визначає URL, який відкривається під час запуску застосунку.
Налаштування іконок та кольорів
Масив icons містить об'єкти зображень з обов'язковими властивостями src, sizes та type. Для Chromium потрібні щонайменше іконки розміром 192×192 та 512×512 пікселів. Властивість purpose зі значенням «maskable» вказує, що зображення розроблене з урахуванням масок і безпечної зони, тому браузер може обрізати будь-яку частину за її межами. Поле screenshots дозволяє додати знімки екрана з обов'язковим параметром form_factor (wide для десктопу, narrow для мобільних) та коректними розмірами.
beforeinstallprompt подія
Браузер генерує подію beforeinstallprompt, коли виявляє можливість встановити вебсайт як PWA. Обробник цієї події викликає preventDefault() для блокування стандартного інтерфейсу встановлення браузера, зберігає посилання на об'єкт BeforeInstallPromptEvent та відображає власний інтерфейс. Подія не спрацює, якщо PWA вже встановлено, застосунок не відповідає критеріям встановлення або пристрій не підтримує встановлення.
Кастомний UI для встановлення
Для створення власного інтерфейсу встановлення збережіть об'єкт події у змінній та викличте його метод prompt() під час натискання на кнопку. Властивість userChoice повертає проміс з результатом вибору користувача (accepted або dismissed). Перевірити встановлення можна через CSS-медіазапит display-mode: standalone або метод getInstalledRelatedApps().
Висновок
У цій статті ми розглянули створення прогресивних вебзастосунків у 2026 році, які працюють офлайн завдяки service worker. Ви дізналися про життєвий цикл сервісного воркера, зокрема про реєстрацію, встановлення та активацію. Ми детально розглянули стратегії кешування даних: Cache-First для статичних ресурсів, Network-First для динамічного контенту та Stale-While-Revalidate для балансу між швидкістю і свіжістю.
Водночас ви навчилися реалізовувати офлайн-функціонал у React-застосунках за допомогою Workbox та налаштовувати маніфест для встановлення PWA на пристрої користувачів. Тож заохочуємо вас застосувати ці знання на практиці, щоб створити стійкі та доступні вебзастосунки, які працюватимуть незалежно від якості мережевого з’єднання.
Підписатися на новини
-
ЛайфхакиСкільки коштує ваше масштабування? FinOps-стратегії для критичних моментів
Стратегії оптимізації витрат та аналіз того, як storage tiering, налаштування requests/limits та контроль обсягу логів можуть суттєво вплинути на ваш бюджет.
-
Думка експертаOperational Intelligence - Tech Pulse | Дайджест #3
-
Думка експертаSpring AI vs LangChain4j: який фреймворк обрати для AI в Java
-
Лайфхаки
OpenTelemetry і eBPF для Python-мікросервісів: як працює distributed tracing і з чого почати
-
Думка експертаAI-First JavaScript: нова ера веброзробки
У цій статті ділимося думками про AI-First підхід, інструменти для інтеграції LLM, практичну реалізацію та архітектурні патерни сучасних AI-powered вебдодатків.