- Обзор
- Начинаем
- Современные теги в head
- Service Worker
- Пример push-уведомлений
- Кэш service-worker
- Императивный кэш
1. Обзор
Прогрессивное веб приложение — это хорошо и прекрасно, но как мне использовать его для своего проекта? Давайте оснастим существующий десктопный сайт прекрасными новыми веб функциями, такими как установка, push-уведомления и оффлайн. Эти функции сделают ваш сайт более доступным и неотразимым для пользователей мобильных устройств, а также сделают десктопных пользователей более вовлеченными.
Чему вы научитесь
- Как быстро превратить существующий сайт в Progressive Web App
- Как стать mobile-friendly и сделать Web App Manifest
- Как предложить вовлеченным пользователям установку приложения
- Как посылать push-уведомления вашим пользователям
- Как поддерживать статический и динамический контент в оффлайне
Что вам понадобится
- Chrome 52 или выше
- (Опционально) Подключенное Android устройство с запущенным Chrome 52 или выше
- Базовое понимание git и Chrome DevTools
- Примеры кода и текстовый редактор
Везде, где это возможно, этот кодлаб идет по кратчайшему пути, ссылаясь на более продвинутые ресурсы и другие кодлабы для углубленного изучения. Изучив в данном кодлабе предлагаемые шаги на примере простого сайта, вы научитесь применять их в собственных проектах.
Точка старта
Вы будете модифицировать сайт под названием Dragotchi, в котором вы можете растить и играть с вашим виртуальным драконом. У сайта три страницы (Home, Dragotchi и FAQ). Две статические, а Dragotchi делает простые Ajax запросы к открытому серверу.
Этот сайт desktop-only, у него нет адаптивного дизайна и он, вобщем, устарел. Тем лучше! Нам понадобится всего несколько шагов, чтобы улучшить его.
Вы можете выполнить в этой лабораторной работе столько, сколько захотите! Каждый раздел научит вас чему-то новому, что будет вам полезно.
2. Начинаем
Давайте начнем с того, что проверим сайт Dragotchi и настроим ваше рабочее окружение.
Что вам понадобится
Клонируйте репозиторий GitHub из командной строки:
1 |
$ git clone https://github.com/googlecodelabs/migrate-to-progressive-web-apps.git |
migrate-to-progressive-web-apps
содержащая код для каждого шага. Для этого кодлаба используйте папку work
и вносите все изменения там.Для проверки кода, вам понадобится сервер Simple HTTP Server application (или например, локальный Python сервер или какой либо другой HTTP сервер) для работы с папкой work
на порту 8887 — если только вы его не изменили.
Теперь вы можете открыть URL и поиграть с 🐲. Не волнуйтесь, кодлаб подождет.
Просмотр сайта на мобильном
Если у вас есть подключенное Android устройство, вы можете (на вашем компьютере) открыть URL: chrome://inspect
(вам надо скопировать и вставить это в адресную строку). Затем, пропишите порт, как показано ниже, чтобы перенаправить порт на тот же порт на мобильном. Нажмите enter для сохранения.
(Примечание перев. Также необходимо, чтобы на Android устройстве был включен режим разработчика и отладка по USB, иначе ничего работать не будет.)
Теперь вы должны получить доступ к простой версии сайта Dragotchi по адресу http://localhost:8887/ на вашем мобильном телефоне. Версия на вашем мобильном телефоне не оптимизирована для такого экрана — мы займемся этим в следующем разделе.
3. Современные теги в head
migrate-to-progressive-web-apps/step3-manifest
.Первое, что мы сделаем для этого старомодного сайта — сделаем его mobile-friendly и включим то, что называется Web App Manifest. Файл манифеста описывает мета информацию о сайте, например то, как он будет выглядеть при добавлении на домашний экран пользователя.
У сайта Dragotchi три HTML страницы. Так как он не использует никакой системы для управления шаблонами, вам надо добавить следующие строки в раздел <head> каждой страницы — index.html, faq.html и dragon.html
1 2 3 4 |
<head> <meta name="viewport" content="width=device-width, user-scalable=no" /> <link rel="manifest" href="manifest.json" /> </head> |
Если вы торопитесь, то можете добавить эти теги только на странице index.html. Но знайте, что на реальных сайтах вам нужно будет добавить их везде.
Окно просмотра (viewport)
Первой строкой идет тег meta
, описывающий вьюпорт. Подробнее о нем вы можете прочитать на favourite search engine, а сейчас, если вы перезагрузите сайт (на вашем андройд телефоне, или используя панель разработчика), вы увидите, что страница сайта корректно умещается на экране.
Манифест веб приложения
Вторая строка добавляет Web App Manifest, который нужен для управления тем, в каком виде ваш сайт добавляется на домашний экран. Он заменяет старые мета-теги, вроде mobile-web-app-title
. В коде страницы мы сослались на этот файл — давайте теперь его сделаем!
Откройте текстовый редактор. Мы напишем в формате JSON. Начнем с чего-то подобного:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "name": "The Most Awesome Dragon Site", "short_name": "🐉🐉🐉", "display": "minimal-ui", "start_url": "/", "theme_color": "#673ab6", "background_color": "#111111", "icons": [ { "src": "icon-192.png", "sizes": "192x192", "type": "image/png" } ] } |
Поле short_name
определяет то, что появится на домашнем экране пользователя: постарайтесь задать строку настолько минимальной, насколько это возможно, название больше 15 символов может быть обрезано. В нашем случае мы использовали эмодзи 🎆 и 🌋, так как они абсолютно валидны!
Стоит запомнить:
- Chrome и другие браузеры очень придирчиво относятся к JSON: не добавляйте лишние запятые и всегда используйте двойные кавычки
- По хорошему вы должны определять несколько иконок под разные размеры экрана — большие иконки, например, могут показываться неправильно на маленьких экранах
- Есть и другие свойства, которые вы можете использовать, найти их можно тут
Сохраните файл как manifest.json
. Перезагрузите страницу на вашем Android устройстве, зайдите в меню в верхнем правом углу и выберите «Add to Home Screen». На вашем домашнем экране должно появиться новое приложение с драконами!
Если у вас нет под рукой андройд устройства, выберите Application
в Chrome Developer Tools. Нажмите Manifest
, чтобы увидеть детали манифеста. Если вы видите No manifest detected
, убедитесь, что вы создали и сохранили файл manifest.json
в папке work
.
Вау! Это прекрасно!
Я знаю. Давайте двинемся дальше и добавим Service Worker!
4. Service Worker
Готовый код находится в migrate-to-progressive-web-apps/step4-sw
.
Service Worker — это скрипт, работающий в фоновом режиме, который браузер может запускать даже тогда, когда пользователь не находится на вашей странице. Он предоставляет оффлайновую поддержку и просыпается при получении уведомления.
Даже пустой Service Worker может помочь вашему сайту. Как получить такую прекрасную штуку? Читайте дальше.
Добавляем Service Worker в код
Сначала нам надо создать новый JavaScript файл. Давайте скопируем самый минимум в новый файл
1 2 3 4 |
/** An empty service worker! */ self.addEventListener('fetch', function(event) { /** An empty fetch handler! */ }); |
Сохраните это как sw.js
! Вот и все, все сделано! Вы создали service worker! 🎆
Регистрация Service Worker
Что, есть еще один шаг? Ну… ладно. На код, приведенный выше, и сам файл (sw.js) еще никто не ссылается. Вам обязательно надо его зарегистрировать, то есть прописать в коде вашего сайта.
Сайт Dragotchi уже содержит скрипт, который запускается на каждой странице. Откройте site.js
(еще один пустой файл) и добавьте код регистрации
1 2 3 |
navigator.serviceWorker && navigator.serviceWorker.register('./sw.js').then(function(registration) { console.log('Excellent, registered with scope: ', registration.scope); }); |
Вот и все! Этот код будет исполняться на каждой загружаемой странице. Если Service Worker уже зарегистрирован, ваш браузер проигнорирует запрос и проверит изменения чуть позднее.
Перезагрузите страницу и проверьте в chrome://serviceworker-internals/
то, что скрипт для сайта действительно загрузился.
Это выглядит примерно так:
Прекрасно, но что мне с этим делать?
Во-первых, и что самое интересное, ваш сайт теперь будет сам напоминать пользователям об установке его на домашний экран, если тот покажет определенный уровень вовлеченности. Чтобы узнать больше, в том числе и о том, как всегда показывать такое предложение, прочитайте эту статью на сайте Google Developers.
Во-вторых, вы можете расширить Service Worker так, чтобы ваш сайт поддерживал push-уведомления и работал в офлайне. Читайте дальше!
5. Пример push-уведомлений
Готовый код находится в migrate-to-progressive-web-apps/step5-push
, также вам необходимо добавить publicKey
из Push Companion внутри site.js
.
Давайте сделаем поддержку push-уведомлений! Мы не станем разбираться с сервером отправки сообщений, а просто зарегистрируем его на вашем сайте, и вы сможете управлять им из командной строки.
Вам нужно сгенерировать ключи — один публичный и один приватный. Откройте Push Companion и не закрывайте, чуть позже мы воспользуемся сгенерированными ключами.
Подписка на push
Внутри кода вашего сайта вам необходима подписка на push-уведомления. На самом деле это довольно просто. Давайте добавим код подписки в site.js
1 2 3 4 5 6 |
navigator.serviceWorker && navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) { serviceWorkerRegistration.pushManager.getSubscription() .then(function(subscription) { // subscription will be null or a PushSubscription }); }); |
Если subscription
равен null
, то нам нужно зарегистрироваться — замените комментарий следующим кодом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
if (subscription) { console.info('Got existing', subscription); window.subscription = subscription; return; // got one, yay } const applicationServerKey = urlB64ToUint8Array(publicKey); serviceWorkerRegistration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey, }) .then(function(subscription) { console.info('Newly subscribed to push!', subscription); window.subscription = subscription; }); |
Во-первых, обратите внимание, что publicKey
нигде не определяется. Создайте его в верхней части файла, используя открытый ключ из Push Companion созданный ранее:
1 |
const publicKey = '<public key>'; |
Теперь вам нужно сохранить файл и перезагрузить страницу — на десктопе или мобильном устройстве, не имеет значения. Если вы посмотрите в консоль разработчика, то увидите сообщение в логах о подписке, содержащее поле «endpoint». Мы используем его в дальнейшем, так что не закрывайте окно.
Если вы видите ошибку, убедитесь, что ваш publicKey
выглядит как длинная строка с кодировкой base64, а не «<открытый ключ из Push Companion>».
Показываем уведомление
Подписка это хорошо, но что должен делать ваш сайт при получении сообщения? Скорее всего сайт Dragotchi не будет загружен у пользователя все время, поэтому вам надо добавить код, который обрабатывает сообщения в вашем Service Worker. Если вы помните, мы ранее говорили о том, что он может запускаться в любое время.
Откройте sw.js и добавьте секцию:
1 2 3 4 5 6 |
self.addEventListener('push', function(event) { event.waitUntil( self.registration.showNotification('Got Push?', { body: 'Push Message received' })); }); |
Это одно из самых простых уведомлений, которые мы можем показывать. У него есть заголовок и простое тело письма. Если хотите больше возможностей, посмотрите документацию. А сейчас перезагрузите страницу, чтобы посмотреть, как это сработает.
Сводим все вместе
Дальше, давайте откроем командную строку (используйте ⌘-⌥-J или Ctrl-Alt-J, чтобы открыть его, если вы еще этого не сделали) и используем curl
для составления правильного HTTP запроса. Он позволит вашему устройству проснуться и запустить код, который мы только что добавили для показа уведомления.
Скопируйте следующий код и вставьте его в консоль внутри Инструментов разработчика, как если бы вы запускали код, набрав его самостоятельно:
1 2 3 4 5 6 7 8 9 10 11 |
var privateKey = window.prompt('Enter Private Key from Push Companion'); privateKey && prepareAuthorization(publicKey, privateKey, subscription.endpoint, 'mailto:example@example.com').then(auth => { const curl = `curl "${subscription.endpoint}" ` + `-X POST ` + `-H "Authorization: ${auth}" ` + `-H "Crypto-Key: p256ecdsa=${publicKey}" ` + `-H "TTL: 100" ` + `-H "Content-Length: 0"`; console.warn('Copy and paste the following into a command-line-'); console.dir(curl); }); |
Это немедленно предложит вам секретный ключ от Push Companion. Введите его. Как только вы нажмете «ОК», консоль отобразит очень длинную строку. Идите вперед и дважды щелкните по ней, скопируйте и вставьте в ту же самую командную строку, чтобы запустить его.
prepareAuthorization
, находящийся в crypto.js
, который примерно соответствует первой части статьи Matt Gaunt Web Push Protocol, для подписания запроса с помощью вашего закрытого ключа.И, вуаля
Вы должны увидеть нечто подобное на вашем клиенте — на компьютере или в мобильном устройстве. Хорошая работа!
Мы прошлись только по верхам. Данные, полученные от клиента, вы можете отправить на ваш бэк-энд или в другой сервис, который может вызвать получение пуш-уведомлений в любой момент, даже когда ваш сайт не открыт.
6. Кэш Service Worker
Готовый код находится в migrate-to-progressive-web-apps/step6-cache
(который не включает Push Notifications).
Давайте используем этот новый Service Worker и убедимся в том, что наш сайт Dragotchi может работать в офлайне. Во-первых, давайте снова откроем скрипт sw.js. Для начала нам понадобится объект кэша, который принадлежит типу Cache—
1 2 3 4 5 6 |
self.addEventListener('install', function(e) { e.waitUntil( caches.open('the-magic-cache').then(function(cache) { }); ); }); |
Теперь, когда он у нас есть, давайте обновим код и добавим весь сайт в кэш
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
self.addEventListener('install', function(e) { e.waitUntil( caches.open('the-magic-cache').then(function(cache) { return cache.addAll([ '/', '/index.html', '/dragon.html', '/faq.html', '/manifest.json', '/background.jpeg', '/construction.gif', '/dragon.png', '/logo.png', '/site.js', '/dragon.js', '/styles.css', ]); }) ); }); |
Достаточно длинно, но это важно. Если у вас сайт создается каким-нибудь процессом, то, вероятно, он сможет сгенерировать и список всех URL, которые надо загрузить пользователю.
Согласование запросов
Все это хорошо, но сейчас вы в момент установки приложения закэшировали весь контент. Контент в этом кэше на самом деле не соответствует HTTP запросам — это следующий шаг! Давайте проделаем простую работу, также внутри sw.js
1 2 3 4 5 6 7 |
self.addEventListener('fetch', function(event) { event.respondWith( caches.match(event.request).then(function(response) { return response || fetch(event.request); }) ); }); |
Объект Cache будет соответствовать вашему запросу, возвращая правильный ответ или null
. Если он null, то мы обращаемся к методу fetch
для выполнения настоящего запроса.
Вы можете написать гораздо более сложные функции. Два этих блока представляют простой кэш — вы храните ассеты прямо по URL и получаете их по URL. Но ничто не мешает вам написать более сложный JavaScript — мы коснемся этого в следующем шаге.
Пробуем
Зайдите на свой телефон и удалите существующее приложение Dragotchi при помощи длительного нажатия на него. Потом снова загрузите его в Chrome, обновите страницу, зайдите в меню справа вверху и нажмите «Add to Home Screen».
Если сейчас вы отключите Android устройство от USB кабеля и тапните на иконку на домашнем экране, то Dragotchi должен загрузиться!
Некоторые предостережения
Этот кэш, хоть это и прекрасно, закэширует ваш сайт навсегда!
До конца времен*
*или пока не кончится место на диске
А это уже не здорово. Вы должны изучить Workbox, который представляет собой набор библиотек, выпущенных Google, чтобы помочь вам создать Progressive Web Apps и написать Service Worker код лучше.
Пока же загрузите http://localhost:8887/clear.html
и нажмите кнопку «Clear». Эта страница представляет собой тестовую страницу, содержащуюся в исходном коде, которая помогает очистить кэш, созданный событием «install» службы Service Worker.
Делать это после каждого изменения может быть не очень удобно. Это основная причина, из-за которой мы оставили этот шаг на конец. Если у вас нет ясной стратегии кэширования, то это может негативно сказаться на ваших пользователях — они могут никогда не получить новую версию вашего сайта. Часто бывает полезно полностью отключить событие fetch
в процессе разработки, так чтобы вы всегда получали самую последнюю версию сайта.
Основное правило: если ваш сервис воркер изменится, ваша страница перезагрузит его и переустановит. Во время разработки вы можете добавить простой комментарий, содержащий «версию» вашего сервис воркера. Всякий раз, когда он изменяется, событие install
будет происходить снова, кэшируя все другие ресурсы, которые вы, возможно, изменили. Добавьте комментарий сейчас, в верхней части файла sw.js-
1 |
// version: I'm nearly finished the codelab woo! |
Далее
Итак, мы немножко смухлевали — если вы загружаете страницу и растите дракона, кликая на действия, то это работает только пока ваш телефон находится в Сети. Запросы обрабатываются внешним сервером, предоставленным для этого кодлаба — он вне вашего сайта, настоящий URL в интернете.
Вы можете увидеть это переведя ваш телефон в авиарежим, в результате чего вы получите alert
или failure
.
Давайте исправим это.
7. Императивный кэш
Готовый код находится в migrate-to-progressive-web-apps/step6-cache
(который не включает Push Notifications).
Как мы уже упоминали, есть гораздо больше возможностей с Cache в Service Worker. Давайте обработаем новый URL. Состояние вашего дракона мы получаем с внешнего сайта, так что мы можем просто перехватить это.
Внутри блока fetch
в sw.js давайте посмотрим на запрос определенного URL:
1 2 3 4 5 6 |
self.addEventListener('fetch', function(event) { if (event.request.url == 'https://dragon-server.appspot.com/') { return; } /** previous code */ }); |
Это показывает нам, что Service Worker на самом деле может перехватывать запросы к URL вне домена вашего сайта. Теперь давайте сделаем что-нибудь с обработкой этого URL
1 2 3 4 5 6 7 8 |
if (event.request.url == 'https://dragon-server.appspot.com/') { console.info('responding to dragon-server fetch with Service Worker! 🤓'); event.respondWith(fetch(event.request).catch(function(e) { let out = {Gold: 1, Size: -1, Actions: []}; return new Response(JSON.stringify(out)); })); return; } |
Это вернет валидное, но ограниченное состояние при обращении к URL, которое вызвано с ошибкой (посмотрите блок catch
этого Promise
). Этот кодлаб всего лишь быстрое введение в тему — который не затрагивает возможности, включающие кэширование, или хранения состояния офлайн в indexDB.
Наша цель — продемонстрировать, что у вас все под контролем.
Пробуем
Как и в предыдущем случае, убедитесь, что вы очистили кэш. Откройте http://localhost:8887/clear.html для этого. Затем удалите приложение с вашего домашнего экрана, перезагрузите страницу в Chrome и снова установите приложение.
Включите авиарежим, затем загрузите сайт через иконку и перейдите на страницу Dragotchi. Вы увидите дефолтное состояние, которое вы прописали в Service Worker и которое он обработал!
8. Вы все сделали
Поздравляем
Вы узнали много интересных вещей, которые позволят вашему прекрасному сайту стать чуть более современным, даже если он и создавался относительно давно!
Теперь попробуйте другие кодлабы, которые раскроют вам больше деталей по всем этим темам. Или прочитайте больше о Service Worker и Push уведомлениях.
Не совсем точный перевод кодлаба на codelabs.developers.google.com