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 MB, не дава десетки разрешения. Той просто отваря сайта и браузърът предлага да инсталира приложението. Ако потребителят откаже — той все пак използва сайта. А ако се съгласи — иконата се появява на началния екран и приложението се отваря като нативно: без адресната лента на браузъра, в режим на цял екран и с бързо стартиране.
Технически 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 регистриран:', registration.scope);
})
.catch(error => {
console.log('SW регистрацията не успя:', 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 позволен');
}
});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 шаблон.
ЧЗВ
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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също