click fraud detection
click fraud detection
Blog Case

Конвертируем сайт в PWA приложение

BLOG
CASE
902
0
5/ 5stars
5/5

В данной статье мы рассмотрим базовые основы прогрессивных веб-приложений (PWA), использующий исключительно нативный API-интернет обозревателя (браузера). Мы не будем использовать никакие фреймворки и все что здесь описано может быть использовано для любых движков.

Что нам необходимо:

  • 20 минут свободного времени;
  • базовые знания HTML и JavaScript;
  • Google Chrome for PWA developer tools;
  • Демонстрационное приложение.

Задача

Планируется проведение каких либо конференций, есть их расписание и информация о текущем докладчике.

Приложение состоит из 3-х файлов app.js, style.css, и index.html. HTML-файл содержит весь статический контент. Докладчики и расписание загружается с сервера асинхронно из JSON файла подобно REST API запросам.

Установка

После скачивания архива, (https://github.com/vaadin-learning-center/pwa-tutorial-basic/archive/start.zip) распаковки и перехода внутрь директории запускаем команды установки и запуска вебсерверa от NodeJS.

npm install serve
./node_modules/serve/bin/serve.js

После этого сайт будет доступен по адресу http://localhost:5000/

Превращение вашего сайта в ПВП (прогрессивное веб-приложение) состоит из 2-х шагов:

  • создание манифеста;
  • добавление вокера.

Манифест представляет собой JSON файл (manifest.json), описывающий будущую структуру и настройки вашего приложения.

{
"name": "Название приложения",
"short_name": "Коротко",
"start_url": ".",
"display": "standalone",
"background_color": "#2A3443",
"description": "Описание",
"theme_color": "#2A3443",
"icons": [
{
"src": "./img/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

Это интуитивно понятный файл, описывающий наше приложение, его цвет, иконки и т. д. Мы так же указываем какой URL (относительно того, с которого загрузили приложение) необходимо отображать после того как приложение будет установлено на ваше устройство.

Сохраняем этот файл и добавляем ссылку на него в наш index.html в секцию header.

 

После загрузки манифеста браузером он поймет что мы загрузили не сайт а приложение.

Что бы в этом убедиться откроем консоль по F12 и перейдем во вкладку Application.

После чего выберем пункт Manifest и видим то, что указали в файле манифеста:

Регистрация вокера

Манифест определяет наш сайт как приложение для браузера. Вокер — это оставшаяся часть пазла, которая будет обеспечивать логику работы приложения в том числе в оффлайн режиме.

Вокер написан на чистом JS и служит связующим звеном между нашим приложением и сетью. С вокером (и некоторой поддержкой API как Cahe API) нам становится доступным контроль над поведением приложения при любых ситуациях с сетью.

Зарегистрируем простейший вокер создав файл app.js.

class PWAConfApp {}
window.addEventListener('load', e => {
new PWAConfApp();
registerSW();
});

Тут мы вызываем функцию registerSW() для регистрации, которую опишем позже.

При этом хорошей практикой будет использование задержки до наступления полной загрузки страницы.

Прежде чем зарегистрировать вокера необходимо убедиться что наш браузер его поддерживает. Для этого в определении метода registerSW проверим поддержку асинхронным стилем (используя операторы async и await).

async function registerSW() {
if ('serviceWorker' in navigator) {
try {
await navigator.serviceWorker.register('./sw.js');
} catch (e) {
alert('ServiceWorker registration failed. Sorry about that.');
}
} else {
alert('Not support!');
}
}

Функция async облегчает понимание кода. После проверки мы регистрируем вокера функцией navigator.serviceWorker.register и уведомляем пользователя если браузер этого не поддерживает либо при ошибке регистрации.

Перегрузим браузер и взглянем на ситуацию в консоле.

Как видно наш вокер указан в секции Service Workers.

Для того чтоб сделать работу комфортней отметим «Update on reload». Дело в том, что вокер обычно загружается один раз независимо от кол-ва открытых вкладок, поэтому мы не увидим изменений при обновлении страницы.

Теперь у нас есть зарегистрированный вокер и мы можем попробовать закешировать некоторый контент.

Кэширование и обслуживание статики

Сервис вокер полностью управляем событиями. Это значит что никакой из участков кода не запустится пока не произойдет событие, к нему подвязанное. Основные интересующие нас события это install и fetch. Мы можем отслеживать их добавив следующие строки кода в файл sw.js

self.addEventListener('install', async event => {
console.log('install event')
});


self.addEventListener('fetch', async event => {
console.log('fetch event')
});

Теперь мы видим что вокер запущен и отписал в консоль наши сообщения.

Любые изменения в файле sw.js запускают новое событие intall и позволяют обновить приложение.

Теперь определим две константы в которых обзовем наш кеш и определим массив файлов для кэширования.

const cacheName = 'pwa-conf-v1';
const staticAssets = [
'./',
'./index.html',
'./app.js',
'./styles.css'
];

Обратите внимание на относительные пути к файлам. Это пригодится в случаях нахождения вашего приложения на внутренних страницах сайта. Так же мы должны явно указать два пути ./ и ./index.html даже если результат их загрузки будет одинаков.

Теперь пришло время закешировать наш массив.

self.addEventListener('install', async event => {
const cache = await caches.open(cacheName);
await cache.addAll(staticAssets);
});

Где мы создаем объект кэша с новым именем и говорим ему добавить в кеш наши пути.

Функция addAll не сработает если один из путей будет недоступен.

Обновим страницу и откроем в консоле секцию Cache Storage и увидим наши данные.

Мы так же можем дебажить приложение из вкладки Sources.

Отдача закешированного контента

Сейчас вся статика закэширована и готова к отдаче. Следующим шагом мы должны внедриться в событие fetch и отдать эти файлы клиенту.

self.addEventListener('fetch', event => {
const req = event.request;
event.respondWith(cacheFirst(req));
});

Тут мы забираем запрос из события и говорим событию вызвать функцию cacheFirst где отдадим элемент из кэша, соответствующий поступившему запросу.

async function cacheFirst(req) {
const cache = await caches.open(cacheName);
const cachedResponse = await cache.match(req);
return cachedResponse || fetch(req);
}

Тут мы открываем именованный кеш. Затем пытаемся получить его элемент по значению запроса, в данном случае кэш представляет собой базу данных с парами ключ-значение, где в качестве ключа используется url запроса. И наконец мы возвращаем элемент кэша иначе передаем за это ответственность сети (делегируем).

Обновляем страницу и убеждаемся что все работает как надо. При этом что бы эмулировать отсутствие соединения отмечаем галочку Offline.

Динамическое кеширование

Несмотря на то что наше приложение работает, оно по прежнему грузит динамический контент из сети, которого пока нет в кэше. Исправим это.

Логика будет следующей. Сперва мы загрузим JSON из сети и получим актуальную версию данных. Затем сохраним их в кэш перед отдачей. Если же сеть не доступна мы берем данные из кэша. Такой алгоритм называется networkingFirst.

Мы предполагаем что картинки статичные, поэтому к ним применим алгоритм cacheFirst, который описали ранее но с изменениями по определению типа ресурса.

self.addEventListener('fetch', event => {
const req = event.request;
if (/.*(json)$/.test(req.url)) {
event.respondWith(networkFirst(req));
} else {
event.respondWith(cacheFirst(req));
}
});

Опишем функцию networkFirst.

async function networkFirst(req) {
const cache = await caches.open(cacheName);
try {
const fresh = await fetch(req);
cache.put(req, fresh.clone());
return fresh;
} catch (e) {
const cachedResponse = await cache.match(req);
return cachedResponse;
}
}

Где при помощи функции fetch пытаемся тянуть данные с сети и сохранить их в кеше, клонировав ответ и затем возвратив его клиенту. В случае исключения и отсутствии сети берем последние данные с кэша, соответствующие запросу.

Наконец, обновим функцию chacheFirst, добавив вызов networkFirst вместо fetch в случае отсутствия данных в кэше для того чтоб их закэшировать.

Теперь наше приложение после первого запуска работает независимо от того, есть ли соединение с сетью в последствии.

Несмотря на это наше приложение все еще имеет ряд недостатков:

  • мы должны помнить об обновлении таймстампа вокера каждый раз когда обновляются статические файлы;
  • процесс инициализации может упасть при недоступности одного ресурса;
  • нет возможности обновить лишь ту статику, которая изменилась;
  • динамическое кэширование никогда не прекращается и может разрастись.

В дальнейшем мы можем использовать инструмент Workbox для решения этих проблем.

5/5
Проголосовало людей: 5
СОДЕРЖАНИЕ
СТАТЬИ
Задача
Установка
Регистрация вокера
Кэширование и обслуживание статики
Отдача закешированного контента
Динамическое кеширование
ПОЛУЧАТЬ ИНТЕРЕСНЫЕ СТАТЬИ
Уже подписались 244 человек
Автор
902
0
Дмитрий Жариков
Дмитрий
Жариков
most
Popular
Возможно
Школа Мастерства агентства Wezom стремится дать своим курсантам качественные, наилучшие знания в сфере IT технологий.…
Анна Томенчук
Анна Томенчук
Статья о том, какие показатели нужно изменить, чтобы повысить конверсию и продажи с сайта.
Галина Назарова
Галина Назарова
Чего ожидать от SEO в 2018.
Галина Назарова
Галина Назарова
Давайте начнем
беседу!
КОММЕНТАРИИ0
ОСТАВИТЬ КОММЕНТАРИЙ К СТАТЬЕ
ПОДПИСЫВАЙТЕСЬ НА РАССЫЛКУ АЙТЫЖБЛОГ
ХОТИТЕ ПОЛУЧАТЬ 
ИНТЕРЕСНЫЕ СТАТЬИ?
Уже подписались 244 человек
313
ПОПИСЧИКОВ
ЧИТАТЬ
4295
ПОПИСЧИКОВ
СЛЕДИТЬ
9307
ПОПИСЧИКОВ
СЛЕДИТЬ