От автора: есть много статей о PWA, в которых подробно рассказывается, что это такое и как его настроить. Тем не менее, вы не всегда можете понять их с первого раза. Моя цель — предоставить основную информацию на простом языке, которой вам будет достаточно, чтобы понять, что такое PWA. Затем я покажу вам реальный пример, чтобы закрепить полученные знания. Таким образом, возьмите свой ноутбук и следите за мной.
Прочитав эту статью, вы поймете:
Преимущества и недостатки PWA.
Основную концепцию.
Как настроить эту технологию на вашем сайте.
Как обновить кэш.
Преимущества и недостатки
Как и у любой технологии, у PWA есть свои преимущества и недостатки. Прежде чем рассказать о них, давайте разберемся, что такое PWA.
Технология PWA была анонсирована Google в 2015 году. Она позиционируется как дополнительная надстройка, позволяющее сделать сайт похожим на мобильное приложение. При этом интерфейс не меняется, не трансформируется, сайт остается прежним, трансформируется только браузер. Кроме того, вы должны знать, для какого типа бизнеса PWA является лучшим решением, чем мобильное приложение.
Что может PWA?
Отправка уведомлений, кэширование контента и установка ярлыка на рабочий стол;
Отправка уведомлений в виде всплывающего окна, где вы можете уведомить пользователя о чем-либо;
Работа в автономном режиме, то есть без подключения к Интернету, благодаря кешированию контента.
Преимущества PWA
PWA прост в установке. Вам не нужно искать магазины приложений, скачивать что-либо или танцевать с бубном. Просто откройте сайт, нажав на ссылку, появится окно «Установить ярлык на рабочий стол», установите его, и все готово.
Работает на всех более-менее современных устройствах, вам нужен только браузер.
Это позволяет сайту быть более доступным из-за ярлыка на рабочем столе. Вы разблокировали свой телефон, нажали на ярлык, и сайт открылся. Это классно.
Занимает меньше памяти, меньше 1 МБ.
Настройка PWA требует меньше времени на разработку, чем создание мобильного приложения. Нет необходимости писать два одинаковых приложения на Android и IOS. Поэтому это будет намного дешевле для бизнеса.
Более высокая безопасность — все ресурсы передаются только через https.
Стабильная работа. Если есть проблемы с интернетом, контент будет компенсирован из кэша, поэтому сайт всегда будет доступен.
Недостатки PWA
Существует неверное представление о том, что PWA помогает улучшить производительность SEO. Я не согласен с этим!
И первая проблема, с которой вы сталкиваетесь — это SPA, где разметка HTML на странице визуализируется через javascript. До тех пор, пока скрипты не загрузятся (столько, сколько им нужно), макет не будет отображаться, а будет иметь только div с идентификатором «app». Вот как раз в тот момент, когда все становится плохо, происходит SEO-анализ, но страница, как вы понимаете, пуста. И даже если вы добавите +100500 PWA на сайт, они не будут ускорять рендеринг HTML-кода.
И чтобы быть более обоснованными, давайте убедимся в этом на реальном примере. Давайте возьмем сайт https://madops.io, который является одностраничным приложением. Если вы посмотрите на него через view-source: https://madops.io, вы увидите все, что я описал выше.
В других случаях, когда сервер отображает всю HTML-разметку одновременно, проблем не возникает, как, например, здесь view-source: https://maddevs.io.
Отключение ряда функций. Из соображений безопасности такие функции, как управление камерой, отправка SMS-сообщений, доступ к датчикам и т. д., будут недоступны для PWA.
Есть еще некоторые браузеры, которые не поддерживают PWA. Например, push-уведомления на IOS.
Основная концепция
Прежде чем углубляться в настройку PWA, давайте разберемся с ее основными понятиями и компонентами.
Service Worker — это по сути файл скрипта, который отвечает за всю магию. Все запросы браузера проходят через него, что дает много возможностей, например, если нет подключения к Интернету, он возвращает контент из кэша (если он там есть, конечно).
В нем мы обрабатываем различные события, пишем, удаляем файлы из кеша и многое другое. Скрипты запускаются в фоновом режиме, параллельно с приложением.
manifest.json — файл настроек. Там мы указываем, какие иконки использовать, какой текст отображать в ярлыке, в каком формате открывать окно браузера и т. д. Давайте поговорим об этом более подробно ниже.
Application Shell — это название оболочки для PWA. В частности, это браузер, который немного трансформирован, чтобы дать больше возможностей разработчику.
HTTPS. Одним из основных требований PWA является передача данных по протоколу https, который является более безопасным. Вы можете использовать для разработки localhost.
Push-уведомления — технология отправки push-уведомлений.
Настройка PWA
PWA очень прост в настройке. Итак, начнем прямо с написания кода! Нет, подождите. Вот ссылка на готовый код https://github.com/denisoed/PWA-example. Там вы можете скачать картинки, которые потребуются в дальнейшем.
Во-первых, вам нужно создать папку в проекте и назвать ее, например, PWA. Затем добавьте его в эту папку index.html, которая будет содержать следующий код:
<!doctype html><html lang="en"> <head> <meta charset="utf-8"> <title>PWA</title> <meta name="description" content="Progressive Web Apps"> </head><body class="fullscreen"> <div class="container"> <a href="https://maddevs.io" target="_blank"> <img src="./images/logo.svg" alt="Mad Devs"> </a> <h1>PWA</h1> <p>Progressive Web Apps</p> </div> </body></html>
Я уже подготовил макет, но без стилей он выглядит плохо, поэтому мы добавим и их. Создаем папку CSS, куда мы добавляем файл styles.css и вставляем в файл следующий код:
body { font-family: sans-serif; } /* Make content area fill the entire browser window */ html, .fullscreen { display: flex; height: 100%; margin: 0; padding: 0; width: 100%; background-color: #000; } /* Center the content in the browser window */ .container { margin: auto; text-align: center; } .container img { width: 50px; height: auto; } .container h1 { color: #fff; font-size: 12rem; font-weight: bold; margin: 30px 0 -20px; } .container p { color: #fff; font-size: 3rem; margin: 0; }
Затем подключите этот файл к index.html в теге head.
<link rel="stylesheet" href="css/styles.css">
Давайте сразу подключим необходимые изображения, которые можно скачать здесь. Нажмите на ссылку, там будет кнопка Clone or download, нажмите ее, затем выберите Download ZIP. Архив будет загружен, и в папке изображений вы найдете изображения. Фу, кажется, я объяснил это довольно понятно:
Вы открываете проект, создаете там каталог с изображениями, куда вставляете все изображения. Затем откройте index.html и вставьте метаинформацию в тег head.
<link rel="icon" href="images/favicon.ico" type="image/x-icon" /> <link rel="apple-touch-icon" href="images/mstile-150x150.png"> <meta name="theme-color" content="black" /> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-title" content="PWA"> <meta name="msapplication-TileImage" content="images/mstile-144x144.png"> <meta name="msapplication-TileColor" content="#FFFFFF"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
В результате в файле index.html должна быть такая структура:
<!doctype html><html lang="en"> <head> <meta charset="utf-8"> <title>PWA</title> <meta name="description" content="Progressive Web Apps"> <link rel="icon" href="images/favicon.ico" type="image/x-icon" /> <link rel="apple-touch-icon" href="images/mstile-150x150.png"> <meta name="theme-color" content="black" /> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-title" content="PWA"> <meta name="msapplication-TileImage" content="images/mstile-144x144.png"> <meta name="msapplication-TileColor" content="#000"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="css/styles.css"> </head><body class="fullscreen"> <div class="container"> <a href="https://maddevs.io" target="_blank"> <img src="./images/logo.svg" alt="Mad Devs"> </a> <h1>PWA</h1> <p>Progressive Web Apps</p> </div> </body></html>
Теперь осталось посмотреть, что случилось. Я нашел очень удобное расширение Web Server для Chrome, которое запускает локальный сервер, вам нужно его установить, оно нам понадобится дальше. В этом нет ничего сложного, просто укажите папку с проектом, где находится index.html, оно сделает все само. Скопируйте ссылку и вставьте ее в браузер.
Настройка и запуск веб-сервера
И вот что мы получили. Я бы не сказал, что это уже хорошо, но как по мне на данный момент нормально!
Ну, самое сложное, считайте, что сделано, теперь посмотрим, что думает Google о нашей работе. Для этого нажмите клавишу F12 и перейдите на вкладку Lighthouse (до обновления Google эта вкладка называлась Аудит), там будет синяя кнопка Создать отчет, нажмите ее.
После завершения процесса проверки мы увидим следующую картину: элемент, отвечающий за PWA, будет серым. Это означает, что у нас нет никаких настроек.
А если вы прокрутите страницу вниз, вы увидите рекомендации, которым нужно следовать, чтобы PWA работало нормально. Вкладка Lighthouse поможет вам отслеживать ошибки при настройке PWA.
Ну вот мы и добрались до самой интересной части. Во-первых, вам нужно создать в корне проекта файл manifest.json. Мы добавляем в него следующие метаданные:
name — полное имя. Используется в ярлыке приложения;
short_name — сокращенное имя будет использоваться там, где полное имя не подходит;
icons — список иконок, которые будут отображаться в ярлыке установленного приложения;
lang — язык по умолчанию;
start_url — обязательный параметр. Он сообщает приложению, с каких файлов начинать. При открытии приложения браузер всегда будет открывать эту страницу;
display — указывает, в каком формате открывать окно браузера;
background_color — это свойство используется на заставке при первом запуске приложения на мобильном устройстве;
theme_color — устанавливает цвет панели инструментов и может отображаться в окне предварительного просмотра приложения в переключателях задач. theme_color должен соответствовать цвету мета-темы, указанному в заголовке документа. В нашем случае <meta name=»theme-color» content=»black» />.
{ "name": "Progressive Web Apps", "short_name": "PWA", "icons": [ { "src": "images/mstile-70x70.png", "sizes": "70x70", "type": "image/png" }, { "src": "images/mstile-144x144.png", "sizes": "144x144", "type": "image/png" }, { "src": "images/mstile-150x150.png", "sizes": "150x150", "type": "image/png" }, { "src": "images/mstile-192x192.png", "sizes": "310x150", "type": "image/png" }, { "src": "images/mstile-310x150.png", "sizes": "310x150", "type": "image/png" }, { "src": "images/mstile-310x310.png", "sizes": "310x310", "type": "image/png" }, { "src": "images/mstile-512x512.png", "sizes": "310x310", "type": "image/png" } ], "lang": "en-US", "start_url": "/index.html", "display": "standalone", "background_color": "black", "theme_color": "black" }
Этого пока достаточно. Вот описание всех свойств этого файла, когда будет время, прочитайте обязательно. Подключение модуля manifest.json в index.html в теге head.
<link rel="manifest" href="/manifest.json">
Давайте начнем писать скрипты. Создаем папку с именем js, куда добавляем файл main.js с этим кодом:
window.addEventListener('load', () => { if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then(reg => { console.log('SW registered!'); }).catch(err => console.log('SW registration FAIL:', err)); } });
Несколько слов о том, что здесь происходит:
Как только страница загружается, мы проверяем, поддерживает ли браузер service Worker, и в случае успеха идем дальше.
Затем регистрируем файл sw.js (который еще не настроен). Ничего необычного в этом нет.
Включаем скрипты в index.html, но уже не в теге head, а перед закрывающим тегом body.
<script src="js/main.js"></script>
Что ж, теперь давайте создадим сам файл sw.js. Он будет хранить всю логику для Service Worker. Создайте его в корне проекта и добавьте имя кэша в качестве первой строки.
const cacheName = 'pwa_v1';
В следующей строке добавьте переменную includeToCache. В ней мы указываем файлы для кэширования. Да, я понимаю, что это не удобно, мне приходится регистрировать все файлы руками, но мы имеем то, что имеем. Однако мы всегда будем уверены, что ничего лишнего не кэшируется. Экономия трафика и стабильность.
const includeToCache = [ '/', '/index.html', '/images/favicon.ico', '/images/logo.svg', '/images/logo-black.svg', '/css/styles.css', '/js/main.js' ];
Дальше переходим к событиям. Service Worker запускает под капотом несколько событий, также называемых жизненным циклом. И первым из них является install. Оно запускается только один раз при записи в кэш.
/* Start the service worker and cache all of the app's content */ self.addEventListener('install', e => { e.waitUntil( caches.open(cacheName).then(cache => { return cache.addAll(includeToCache); }) ); });
Событие Fetch. Это событие сканирует все запросы и, если что-то совпадает с тем, что находится в кэше, возвращает совпадение из кеша. В противном случае оно возвращает то, что поступает с сервера. Метод responseWith отвечает за извлечение данных из кэша или данных, возвращаемых сервером. И если сервер ничего не возвращал, мы берем это из кэша.
/* Serve cached content when offline */ self.addEventListener(‘fetch’, e => { e.respondWith( caches.match(e.request).then(response => { return response || fetch(e.request); }) ); });
Этого кода пока достаточно. Теперь давайте удостоверимся, что файл sw.js зарегистрирован и кэш записан. Перейдите в консоль разработчика, откройте вкладку Приложение и перейдите в настройки Service Workers. Здесь мы видим, что файл sw.js успешно зарегистрирован, это подтверждается зеленым светом.
Мы продолжаем двигаться по боковой панели навигации, находим выпадающий список с именем Cache Storage, где фактически хранится наш кэш. Если вы нажмете на нее, вы увидите, какие файлы и контент были кэшированы.
Теперь, если вы отключите интернет и перезагрузите страницу, сайт будет работать.
Подводя итог. Чтобы сайт работал, когда нет Интернета, вам не нужно устанавливать какие-либо фреймворки, добавлять библиотеки и так далее. Достаточно нескольких строк кода и общего понимания этой технологии.
Как обновить кэш?
Первой проблемой, с которой я столкнулся при работе с PWA, было обновление старого кэша. Но, как оказалось, это очень легко решить.
Давайте изменим пару стилей, чтобы вы могли видеть, что что-то изменилось. Затем обновите страницу и убедитесь, что стили на странице изменились. Мы отключаем интернет и снова загружаем страницу, но по какой-то причине кэш не обновился, и мы видим старую версию сайта.
Решение состоит в том, чтобы добавить в файл sw.js событие activate, при его вызове мы проверим имя старого и нового кэша, а если имена различаются, то удалим старый и добавим новый кэш. Да, для обновления кэша нам нужно менять его имя при каждом обновлении кода.
Сначала я нигде не указывал суффикс *_v1 в имени кеша, это будет его версия. Неважно, как вы это называете, главное, чтобы имена были разные.
self.addEventListener(‘activate’, e => { // delete any caches that aren’t in cacheName // which will get rid of version e.waitUntil( caches.keys().then(keys => Promise.all( keys.map(key => { if (cacheName !== key) { return caches.delete(key); } }) )).then(() => { console.log(cacheName + ‘ now ready to handle fetches!’); }) ); });
Если вы читаете код, вы можете увидеть условие, в котором сравниваются имена кэшей, и если они не совпадают, старый кэш удаляется.
Несколько слов о события activate. Это событие срабатывает после того, как worker был зарегистрирован и готов к работе. Но для того, чтобы он был готов, вам нужно подождать, пока старый кэш больше не будет использоваться сайтом, и это займет некоторое время. И чтобы избавиться от этого ожидания, вы можете добавить следующий метод.
self.skipWaiting();
Теперь кеш будет обновляться сразу после регистрации нового worker. Добавление его в событие install.
/* Start the service worker and cache all of the app's content */ self.addEventListener('install', e => {self.skipWaiting();e.waitUntil( caches.open(cacheName).then(cache => { return cache.addAll(includeToCache); }) ); });
А в файле main.js добавлена функция update, которая будет запускать обновление кэша при каждой перезагрузке страницы.
reg.update();
Добавьте метод console.log(). На самом деле, это не имеет значения, главное, что в обратном вызове есть .then().
window.addEventListener('load', () => { if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then(reg => { reg.update(); console.log('SW registered!'); }).catch(err => console.log('SW registration FAIL:', err)); } });
Вот и все, перезагрузите страницу. Снова откройте инструменты разработчика, перейдите в автономный режим на вкладке Service Workers в боковой панели, снова перезагрузите страницу и просмотрите вкладку Cache Storage. Здесь вы можете увидеть, что старый кеш будет заменен новым.
Установка нового Workers занимает ~ 2 минуты, поэтому может потребоваться перезагрузить страницу несколько раз.
Мы видим наши новые стили и обновленный кэш. Ура!
Заключение
В этой статье мы узнали основную информацию о том, как создать и настроить технологию PWA на реальном примере.
Автор: Denis Grushkin
Источник: https://blog.maddevs.io
Редакция: Команда webformyself.