Минимальная PWA

Дата публикации: 2023-02-27

Любой сайт, независимо от исходной платформы, потенциально может стать PWA. PWA строится на HTML, CSS и JavaScript, поэтому для преобразования сайта достаточно выполнить несколько требований.

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 — частный случай того, как браузер удерживает поведение приложения между запусками. Общая модель этой среды изложена в книге «Браузер как операционная система для разработки современных приложений».