Минимальная PWA
Дата публикации: 2023-02-27Любой сайт, независимо от исходной платформы, потенциально может стать PWA. PWA строится на HTML, CSS и JavaScript, поэтому для преобразования сайта достаточно выполнить несколько требований.
- HTTPS: сайт должен работать по HTTPS.
- Web manifest: браузер получает метаданные PWA.
- Иконка: нужна для размещения приложения на домашнем экране смартфона.
- Service Worker: JavaScript обрабатывает запросы и может ответить при offline-работе.
- App Shell: HTML-код устанавливает и запускает PWA.
Web manifest
Минимальный manifest содержит описание, цвета, иконки и адреса:
{
"background_color": "#000000",
"description": "Simple PWA",
"display": "standalone",
"icons": [
{"sizes": "180x180", "src": "./img/favicon-180.png", "type": "image/png"},
{"sizes": "192x192", "src": "./img/favicon-192.png", "type": "image/png"},
{"sizes": "512x512", "src": "./img/favicon-512.png", "type": "image/png"}
],
"id": "/", "name": "Simple PWA", "scope": "./",
"short_name": "Simple PWA", "start_url": "./", "theme_color": "#000000"
}
Иконка
Иконки представляют приложение на домашнем экране, панели задач и в переключателе приложений. Нужны разные размеры, включая 192x192 и 512x512 для Android и 180x180 для Apple; используйте PNG или SVG.
Service Worker
Чтобы приложение можно было установить на телефон, service worker должен хотя бы обеспечить offline-доступ к app shell. Остальные ресурсы кэшировать необязательно:
'use strict';
const CACHE_STATIC = 'static-cache-v1';
const FILES_TO_CACHE = ['./', './favicon.ico', './img/favicon-512.png', './index.html', './pwa.json'];
function onFetch(evt) {
async function cacheOrFetch(req) {
const cache = await self.caches.open(CACHE_STATIC);
const cached = await cache.match(req);
return cached ?? await fetch(req);
}
evt.respondWith(cacheOrFetch(evt.request));
}
function onInstall(evt) {
evt.waitUntil(Promise.all(FILES_TO_CACHE.map((url) => caches.open(CACHE_STATIC).then((cache) => cache.add(url)))));
}
self.addEventListener('fetch', onFetch);
self.addEventListener('install', onInstall);
App Shell
App shell — главный файл сайта, обычно index.html. Он указывает браузеру manifest и регистрирует service worker:
<link rel="manifest" href="./pwa.json">
<script>
if ('serviceWorker' in navigator) {
self.addEventListener('load', async () => {
const container = navigator.serviceWorker;
if (container.controller === null) await container.register('sw.js');
});
}
</script>
Заключение
Почти любой сайт можно превратить в PWA по этим правилам. Но минимальная PWA лишь помещает иконку сайта на домашний экран. Чтобы использовать современные Web API по-настоящему, приложение с самого начала должно учитывать offline-работу, хранение данных на клиенте и вычисления в браузере.
Дополнительные фрагменты исходного кода
}, {
} ],
}
const CACHE_STATIC = ‘static-cache-v1’; const FILES_TO_CACHE = [
]; function onActivate(evt) {
console.log(SW: send 'claim' message to the clients.);
evt.waitUntil(
self.clients.claim()
);
}
function onFetch(evt) {
async function cacheOrFetch(req) {
const cache = await self.caches.open(CACHE_STATIC);
const cachedResponse = await cache.match(req);
return cachedResponse ?? await fetch(req);
}
console.log(SW: fetch '${evt.request}'.);
evt.respondWith(cacheOrFetch(evt.request));
}
function onInstall(evt) {
async function cacheStaticFiles() {
const cacheStat = await caches.open(CACHE_STATIC);
await Promise.all(
FILES_TO_CACHE.map(function (url) {
return cacheStat.add(url).catch(function (reason) {
console.log('${url}' failed: ${String(reason)});
});
})
);
}
console.log(SW: cache app shell on install.);
evt.waitUntil(cacheStaticFiles());
}
self.addEventListener(‘activate’, onActivate);
self.addEventListener(‘fetch’, onFetch);
self.addEventListener(‘install’, onInstall);
Service Worker и автономная работа PWA — частный случай того, как браузер удерживает поведение приложения между запусками. Общая модель этой среды изложена в книге «Браузер как операционная система для разработки современных приложений».