Хабрахабр

PWA — это просто. Hello Habr

Продолжаем знакомство с Progressive Web Applications. После теоретической прошлой части самое время перейти к практике.

Сегодня мы построим простое, но полноценное PWA «Hello Habr».

При открытии в браузере на мобильном устройстве возможно добавление ярлыка на домашний экран и запуск в полноэкранном режиме.
Если кто хочет попробовать рассматриваемый пример на своем компьютере, то Chrome позволяет работать локально с простыми PWA приложениями без установки сторонних веб серверов с SSL сертификатами.
Приложение доступно по адресу https://altrusl.github.io/habr-pwa/hello-habr/.

Инструкция для запуска "Hello Habr" локально

Необходимо установить из интернет-магазина Chrome вот это или аналогичное расширение, являющееся локальным веб-сервером. Без поддержки PHP, естественно.

Файлы «Hello Habr» можно взять с GitHub-a — https://github.com/altrusl/habr-pwa/tree/master/hello-habr

Поместите всё в одну директорию и укажите ее веб серверу.

Он показывает на ней картинку (лого) и анимированную надпись. «Hello Habr» состоит из одной страницы.

"Hello Habr" code

index.html

<html> <head> <title>Hello Habr</title> <script src="hh.js"></script> <link rel="stylesheet" href="hh.css" /> <script type="text/javascript"> if ('serviceWorker' in navigator) ) .catch(function(error) { console.log('Service worker registration failed, error:', error); }); } </script> </head> <body> <div class="center"> <p id="text"></p> </div> <div id="logo"></div> </body>
</html>

hh.css

@font-face { font-family: Zaplyv-Heavy; src: url(Zaplyv-Heavy.otf); } body { display: flex; align-items: center; align-content: center; justify-content: center; overflow: auto; } .center { font-family: Zaplyv-Heavy; font-size: 8vmax;
} #logo { background-image: url(logo.jpg); background-size: 100%; width: 100px; height: 100px; position: absolute; top: 0; right: 0; margin: 10px;
}

hh.js

window.onload = function() { fetch("hh.txt?mode=nocache").then(data => data.text()).then(data => { animateText(data) });
} function animateText(data) { var ele = document.getElementById("text"), txt = data.split(""); var interval = setInterval(function(){ if(!txt[0]){ return clearInterval(interval); }; ele.innerHTML += txt.shift(); }, 150);
}

hh.txt

Hello Hubr

Также присутствует кастомный шрифт. Итого — минимальный полный набор ресурсов среднего веб сайта. Если открыть index.html в браузере, отобразится картинка и надпись. Надпись загружается javascript-ом через fetch из файла hh.txt — простейшая модель общего PWA приложения.

Добавим к нашим файлам Service Worker. Если открывать без sw.js, то это будет обычный веб сайт.

sw.js

// Caches
var CURRENT_CACHES = { font: 'font-cache-v1', css:'css-cache-v1', js:'js-cache-v1', site: 'site-cache-v1', image: 'image-cache-v1'
}; self.addEventListener('install', (event) => { self.skipWaiting(); console.log('Service Worker has been installed');
}); self.addEventListener('activate', (event) => { var expectedCacheNames = Object.keys(CURRENT_CACHES).map(function(key) { return CURRENT_CACHES[key]; }); // Delete out of date caches event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { if (expectedCacheNames.indexOf(cacheName) == -1) { console.log('Deleting out of date cache:', cacheName); return caches.delete(cacheName); } }) ); }) ); console.log('Service Worker has been activated'); }); self.addEventListener('fetch', function(event) { console.log('Fetching:', event.request.url); event.respondWith(async function() { const cachedResponse = await caches.match(event.request); if (cachedResponse) { console.log("\tCached version found: " + event.request.url); return cachedResponse; } else { console.log("\tGetting from the Internet:" + event.request.url); return await fetchAndCache(event.request); } }()); }); function fetchAndCache(request) { return fetch(request) .then(function(response) { // Check if we received a valid response if (!response.ok) { return response; // throw Error(response.statusText); } var url = new URL(request.url); if (response.status < 400 && response.type === 'basic' && url.search.indexOf("mode=nocache") == -1 ) { var cur_cache; if (response.headers.get('content-type') && response.headers.get('content-type').indexOf("application/javascript") >= 0) { cur_cache = CURRENT_CACHES.js; } else if (response.headers.get('content-type') && response.headers.get('content-type').indexOf("text/css") >= 0) { cur_cache = CURRENT_CACHES.css; } else if (response.headers.get('content-type') && response.headers.get('content-type').indexOf("font") >= 0) { cur_cache = CURRENT_CACHES.font; } else if (response.headers.get('content-type') && response.headers.get('content-type').indexOf("image") >= 0) { cur_cache = CURRENT_CACHES.image; } else if (response.headers.get('content-type') && response.headers.get('content-type').indexOf("text") >= 0) { cur_cache = CURRENT_CACHES.site; } if (cur_cache) { console.log('\tCaching the response to', request.url); return caches.open(cur_cache).then(function(cache) { cache.put(request, response.clone()); return response; }); } } return response; }) .catch(function(error) { console.log('Request failed for: ' + request.url, error); throw error; });
}

Как видно, мы создаем пять кэшей для каждого вида ресурсов. Кэш site — для html файлов. Кэшируются все ресурсы, за исключением тех, у кого в GET query стоит «mode=nocache» — а это у нас запрос к файлу hh.txt со строкой для надписи.

Это бывает частой проблемой при разработке приложений с Service Worker-ом, поэтому дисковый кэш (кэш браузера) лучше отключать. Иногда можно видеть, что ресурс берется с дискового кэша. И не у себя в браузере, а на сервере — например, в

.htaccess

# Cache-Control Headers
<ifModule mod_headers.c> <FilesMatch (\.css|\.js|sprites\.png)$> Header unset ETag Header unset Expires Header set Cache-Control "no-cache" </FilesMatch>
</IfModule>

Логика работы sw.js простая — «Cache falling back to the network». Сперва запрашиваемый ресурс проверяется в кэше, если он там есть, то берется и возвращается браузеру оттуда. Если нет — получается из сети, возвращается браузеру, а копия ресурса помещается в кэш.

После второго открытия в хранилище создаются наши кэши и в них помещаются наши ресурсы. После первого открытия страницы index.html в консоли Chrom-a видны записи об установке и активации Service Worker-а. Также видно, что при последующих открытиях на веб сервер уходят только запросы к hh.txt, все остальные ресурсы берутся из Service Worker-a.

Скриншот

Хранящиеся локально index.html, hh.css, hh.js, hh.otf, logo.jpg — это и есть тот самый application shell, оболочка статичных ресурсов и данных, выполняющая роль оболочки программы на клиенте. Вся динамическая информация, необходимая для работы сайта, получается javascript запросами на сервер и отображением полученных данных в app shell-e. В нашем случае это запрос к text.txt.

Для того, чтобы называться функционально полноценным PWA, «Hello Habr» не хватает одного — иконки на домашнем экране смартфонов и запуска в полноэкранном режиме.

Для этого необходимо в index.html подключить манифест приложения:

manifest.json

{ "short_name": "Hello Habr", "name": "Hello Habr - PWA example", "icons": [ { "src": "logo3.jpg", "type": "image/jpg", "sizes": "192x192" }, { "src": "logo2.jpg", "type": "image/jpg", "sizes": "512x512" } ], "start_url": "/habr-pwa/hello-habr/", "background_color": "#3367D6", "display": "standalone", "scope": "/habr-pwa/hello-habr/", "theme_color": "#3367D6"
}

Подключается он в index.html:

<link rel="manifest" href="manifest.json">

После этого мобильные браузеры (каждый по-своему) предложат создать ярлык для приложения на домашнем экране. При запуске по ярлыку приложение будет открываться в standalone режиме — без браузерных элементов управления. Более подробней об опциях манифеста — на Google Developers.

Как видно, для того, перевести простой сайт в PWA нужно просто подключить манифест и файл Service Worker-a. Приложение «Hello Habr» в минимальной мере обладает всеми свойствами PWA и является им по сути. Используемый sw.js достаточно универсальный.

Причем, sw.js останется практически тем же. В следующий раз переведем в PWA готовый сайт на CMS Joomla (сайт «из коробки» с изначальными демо-данными).

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть