Ваше первое прогрессивное веб-приложение (PWA)

pwa

  1. Введение
  2. Начнем установку
  3. Архитектура оболочки приложения (App Shell)
  4. Реализация оболочки приложения (App Shell)
  5. Старт с быстрого запуска
  6. Использование service workers для предварительного кэширования App Shell
  7. Используем service workers для кэширования прогнозных данных
  8. Поддержка нативной интеграции
  9. Развертываем на безопасном хосте и пляшем
 

1. Введение

Прогрессивные веб-приложения — это опыт, который объединяет лучшее от веб-сайтов и лучшее от приложений. Они полезны пользователям с самого первого открытия вкладки браузера, без необходимости установки. Поскольку пользователь постепенно наращивает взаимодействие с приложением, оно становится все более и более мощным. Приложение быстро загружается, даже в медленных сетях, отправляет релевантные push-уведомления, имеет иконку на домашнем экране и загружается в полноэкранном режиме.

Что такое прогрессивное веб-приложение?

Прогрессивное веб-приложение это:

  • Прогрессивность — работает для каждого пользователя, независимо от выбора браузера, потому что в качестве основного принципа заложено прогрессивное улучшение.
  • Отзывчивость — подходит для любого форм-фактора: десктоп, мобильный телефон, планшет или что-либо еще.
  • Независимость от подключения — с помощью service workers может работать в автономном режиме или в медленных сетях.
  • Подобно приложению — выглядит как приложение, потому что модель оболочки приложения отделяет функциональность приложения от содержимого приложения.
  • Свежесть — всегда обновляется благодаря процессу обновления service worker.
  • Безопасность — обслуживается через HTTPS для предотвращения перехвата или подмены данных.
  • Обнаруживаемый — идентифицируется как «приложение», однако благодаря манифесту W3C и регистрации service worker-а позволяет поисковым системам находить его.
  • Возможность повторного подключения — упрощает повторное взаимодействие с помощью таких функций, как push-уведомления.
  • Устанавливаемость — позволяет пользователям добавлять приложения на свой домашний экран, которые они находят наиболее полезными, без использования магазина приложений.
  • Linkable — легко поделиться приложением по URL-адресу, не требует полноценной установки.

Этот кодлаб проведет вас через создание собственного прогрессивного веб-приложения (Progressive Web App), включая дизайн, а также детали реализации, чтобы ваше приложение соответствовало всем вышеуказанным ключевым принципам PWA.

Что мы будем делать?

В этом кодлабе мы собираемся сделать погодное веб-приложение — Weather, с использованием технологий Progressive Web App.

Ваше приложение будет:

  • Использовать и демонстрировать вышеуказанные принципы Progressive Web Apps.
  • Использовать живые данные о погоде.
  • Предоставлять взаимодействия подобные нативным приложениям, позволяющие пользователю добавлять города.

 

Что вы узнаете?

  • Как спроектировать и создать приложение с помощью метода «app shell»
  • Как заставить приложение работать в оффлайн режиме
  • Как хранить данные для последующего использования в автономном режиме

Что вам потребуется?

  • Последняя версия Chrome. Обратите внимание, что приложение работает и в других браузерах, но мы будем использовать некоторые функции Chrome DevTools, чтобы лучше понять, что происходит на уровне браузера.
  • Web Server for Chrome, или ваш собственный веб-сервер на выбор.
  • Пример кода.
  • Текстовый редактор.
  • Базовые знания HTML, CSS, JavaScript и Chrome DevTools.

Этот кодлаб ориентирован на Progressive Web Apps (PWA). Некоторые понятия и блоки кода не объясняются и предоставляются для простого копирования и вставки в проект.

 

2. Начнем установку

Скачиваем код

Нажмите следующую ссылку, чтобы загрузить весь код для этого кодлаба:

Распакуйте скачанный zip-файл. Файл  распакуется в корневую папку your-first-pwapp-master, которая содержит папку для каждого шага этого кодлаба, а также все необходимые вам ресурсы.

Папки step-NN содержат конечный результат каждого шага этого кодлаба. Они там для справки. Мы будем кодить в каталоге work.

Установка и проверка веб сервера

Вы можете свободно использовать свой собственный веб-сервер, но этот кодлаб рассматривает работу с веб сервером Chrome Web Server. Если у вас еще не установилено это расширение, вы можете установить его через интернет-магазин Chrome Web Store.

После установки расширения Web Server for Chrome, нажмите кнопку Apps в панели закладок:

В следующем окне щелкните иконку Web Server:
Web Server icon
Вы увидите диалог, который позволит вам настроить локальный веб-сервер:
local web server
 

Нажмите кнопку choose folder и выберите рабочую папку. Это позволит вам отслеживать изменения по URL, приведенному в диалоговом окне веб-сервера (в разделе Web Server URL(s)).

В разделе Options установите флажок Automatically show index.html, как показано ниже:

Automatically show index.html

Затем остановите и перезапустите сервер, сдвинув переключатель Web Server: STARTED влево, а затем обратно вправо.

Web Server: STARTED

Теперь посетите свой рабочий сайт в своем веб-браузере (нажав на выделенный Web Server URL), и вы увидите страницу, которая выглядит примерно так:

Web Server URL

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

С этого момента все тесты и проверки (например, разделы Тестирование на последующих этапах) должны выполняться с использованием этих настроек веб-сервера.
 

3. Архитектура оболочки приложения (App Shell)

Что такое app shell?

App shell (оболочка приложения) — это минимальный код HTML, CSS и JavaScript, который необходим для запуска пользовательского интерфейса прогрессивного веб приложения и также является одним из компонентов, который обеспечивает производительность. Его первая загрузка должна быть чрезвычайно быстрой и немедленно кэшироваться. «Кэшироваться» означает то, что файлы оболочки загружаются один раз по сети и затем сохраняются на локальном устройстве. В каждый последующий момент, когда пользователь открывает приложение, файлы оболочки загружаются из кэша локального устройства, что приводит к молниеносному запуску.

Архитектура app shell отделяет ядро инфраструктуры приложения и UI от данных. Весь пользовательский интерфейс и инфраструктура кэшируются локально с помощью service worker, так что при последующих загрузках прогрессивного веб приложения требуется только получение необходимых данных, а не загрузка всего приложения целиком.

Service worker — это скрипт, который работает в фоновом режиме браузера, отдельно от веб-страницы, открывая дверь к возможностям, которые не нуждаются в веб-странице или взаимодействии с пользователем.

App shell

Иными словами, оболочка приложения похожа на тот код, который вы публикуете в магазине приложений (app store) при создании нативного приложения. Это ядро компонентов, необходимых для того, чтобы ваше приложение могло запуститься, но, не содержащее данных.

Зачем использовать архитектуру App Shell?

Использование оболочки приложения (app shell) позволяет сфокусироваться на скорости, придавая вашим прогрессивным веб-приложениям свойства аналогичные нативным приложениям: мгновенная загрузка и регулярные обновления, без использования магазина приложений.

Создаем App Shell

Первый шаг — разбить конструкцию на его основные компоненты.

Спросите себя:

  • Что должно выводиться на экран немедленно?
  • Какие другие UI компоненты являются ключевыми для нашего приложения?
  • Какие вспомогательные ресурсы необходимы для оболочки приложения? Например, изображения, JavaScript, стили и т. д.

Мы собираемся создать приложение Weather в качестве нашего первого прогрессивного веб-приложения. Ключевыми компонентами будут:

  • Хедер с заголовком и кнопками добавления/обновления
  • Контейнер для прогнозов
  • Шаблон карточки прогноза
  • Диалоговое окно для добавления новых городов
  • Индикатор загрузки

При разработке более сложного приложения, контент, который не требуется для начальной загрузки, может запрашиваться позже, а затем кэшироваться для будущего использования. Например, мы могли бы отложить загрузку диалогового окна New City (Новый город) до первого взаимодействия с этим диалоговым окном.

 

4. Реализация оболочки приложения (App Shell)

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

Узнайте больше о Web Starter Kit

Создание HTML для App Shell

Сейчас мы добавим основные компоненты, которые обсуждались в Архитектуре оболочки приложения (App Shell).

Вспомним, что ключевые компоненты будут состоять из:

  • Хедер с заголовком и кнопками добавления/обновления
  • Контейнер для прогнозов
  • Шаблон карточки прогноза
  • Диалоговое окно для добавления новых городов
  • Индикатор загрузки

Файл index.html, который уже находится в вашем рабочем каталоге, должен выглядеть примерно так (это часть всего контента, не копируйте этот код в свой файл):

Обратите внимание, что лоадер виден по умолчанию. Это гарантирует, что пользователи сразу же увидят лоадер при загрузке страницы, что дает им четкое понимание, что контент загружается.

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

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

Добавляем ключевой начальный код JavaScript

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

В вашей рабочей директории уже есть код приложения scripts/app.js, в нем вы найдете:

  • Объект app, который содержит некоторую ключевую информацию, необходимую для приложения.
  • Слушатели событий для всех кнопок в заголовке (add/refresh) и в диалоговом окне добавления города (add/cancel).
  • Метод добавления или обновления карточек прогноза (app.updateForecastCard).
  • Метод получения последних данных прогноза погоды из Firebase Public Weather API (app.getForecast).
  • Метод итерации текущих карточек и вызов app.getForecast для получения последних данных прогноза (app.updateForecasts).
  • Некоторые фейковые данные (initialWeatherForecast), которые используем для быстрого тестирования рендеринга элементов.

Протестируем следующее

Теперь, когда у вас есть основа в виде HTML, стилей и JavaScript, пришло время протестировать приложение.

Чтобы увидеть, как рендерятся фейковые данные о погоде, раскомментируйте следующую строку внизу вашего index.html файла:

Затем раскомментируйте следующую строку в нижней части файла app.js:

Перезагрузите приложение. Результатом должна быть хорошо отформатированная (хотя и фейковая, как вы можете узнать по дате) карточка прогноза с отключенным спиннером, например:

WeatherForecast

После того, как вы попробуете приложение и проверите, что оно работает так, как ожидалось, вы можете удалить вызов app.updateForecastCard с фейковыми данными. Нам это нужно было только для того, чтобы проверить, все ли работает так, как ожидалось.

 

5. Старт с быстрого запуска

Прогрессивные веб приложения должны запускаться быстро и быть юзабельными сразу же. В текущем состоянии наше приложение Weather App запускается быстро, но оно безполезно, так как данных нет. Мы могли бы сделать AJAX запрос для получения этих данных, но это приводит к дополнительному запросу и увеличивает первоначальную нагрузку. Вместо этого давайте представим реальные данные при первой загрузке.

Внедрение данных прогноза погоды

Для этого кодлаба мы будем моделировать сервер, вводящий прогноз погоды непосредственно в JavaScript, но в реальном приложении последние данные прогноза погоды будут вводиться сервером на основе геолокации пользователя на основе IP-адреса.

Код уже содержит данные, которые мы будем вставлять. Это initialWeatherForecast, который мы использовали в предыдущем шаге.

Дифференциируем первый запуск

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

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

Дополнительная информация: замените реализацию localStorage на idb, посмотрите localForage как простую обертку для idb.
Сначала давайте добавим код, необходимый для сохранения пользовательских настроек, в конец немедленно вызываемой функции. Найдите в своем коде комментарий TODO.

Ниже комментария добавьте следующий код.

Затем добавим код, проверяющий, есть ли у пользователя какие-либо сохраненные города и выводящий их, или использющий введенные данные. Найдите следующий комментарий:

Ниже этого комментария добавьте следующий код:

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

Сохранение выбранных городов

Наконец, чтобы сохранять выбранный город в локальном хранилище, вам нужно изменить обработчик кнопки add city.

Обновите обработчик clickAddCity, чтобы он соответствовал следующему коду:

Добавлена проверка app.selectedCities на существование, а также добавлены вызовы app.selectedCities.push() и app.saveSelectedCities().

Протестируем

  • При первом запуске ваше приложение должно немедленно показать пользователю прогноз от initialWeatherForecast.
  • Добавьте новый город (щелкнув на иконку + в правом верхнем углу) и убедитесь, что вторая карточка выводится.
  • Обновите браузер и убедитесь, что приложение загружает оба прогноза и показывает самую последнюю информацию.

 

6. Использование service workers для предварительного кэширования App Shell

Прогрессивные веб-приложения должны быть быстрыми и инсталлируемыми, что означает, что они работают в режиме онлайн, оффлайн и на прерывистых, медленных соединениях. Чтобы достичь этого, нам нужно кэшировать нашу оболочку приложения с помощью service worker.

Если вы не знакомы с service worker, вы можете прочитать Введение В Service Workers, где написано о том, что они могут делать, как работает их жизненный цикл и многое другое. После того, как вы закончите этот кодлаб, обязательно загляните в Debugging Service Workers code lab для более глубокого понимания, как работать с service worker.

Функции, предоставляемые service worker, следует рассматривать как прогрессивное улучшение и добавлять, только если они поддерживаются браузером. Например, с service worker вы можете кэшировать оболочку приложения и данные для своего приложения, чтобы все это было доступно при отсутствии сети. Когда service worker не поддерживаются, оффлайн код не вызывается, и пользователь получает базовый функционал. Использование функции обнаружения для обеспечения прогрессивного улучшения не сильно повлияет на производительность и не будет ломаться в старых браузерах, не поддерживающих эту функцию.

Помните: функции service worker работают только на страницах, к которым обращаются через HTTPS (http://localhost и его эквиваленты также будут работать, дабы облегчить тестирование). Чтобы понять рациональность этого ограничения, ознакомьтесь с Prefer Secure Origins For Powerful New Features от команды Chromium.

Регистрация service worker если он доступен

Первым шагом к оффлайн приложению является регистрация service worker, скрипт, который обеспечивает фоновую работу без надобности открытой веб-страницы или взаимодействия с пользователем.

Это выполняется двумя простыми шагами:

  • Сообщите браузеру зарегистрировать JavaScript  файл в качестве service worker.
  • Создайте JavaScript файл, содержащий service worker.

Во-первых, нам нужно проверить, поддерживает ли браузер работу service worker, и если да, регистрируем service worker. Добавьте следующий код в app.js (после комментария // TODO add service worker code here):

Кэширование ресурсов сайта

После регистрации service worker, при первом запуске пользователем на странице запускается событие install. В обработчике этого события мы будем кэшировать все данные, необходимые приложению.

Код ниже НЕ должен использоваться в продакшене, он охватывает только самые основные сценарии, и легко может привести к ситуации, когда оболочка приложения никогда не будет обновляться. Обязательно просмотрите приведенный ниже раздел, в котором обсуждаются подводные камни этой реализации и способы их устранения.

Service worker должен открыть объект caches и заполнить его ресурсами, необходимыми для загрузки оболочки приложения. Создайте файл в корневой папке приложения (в папке your-first-pwapp-master/work) с именем service-worker.js. Этот файл должен находиться в корне приложения, поскольку область действия для сервис воркеров ограничивается той директорией, в котором располагается сам файл. В файл service-worker.js добавьте следующий код:

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

После открытия кэша, мы вызываем cache.addAll(), который запрашивает данные с сервера по списку URL-адресов и добавляет ответы в кэш. К сожалению, cache.addAll() является атомарным, т.е. если фейлится какой-либо из файлов, то с ошибкой завершается весь процесс!

Хорошо, давайте разберемся, как вы можете использовать DevTools для понимания и отладки сервис воркеров. Перед перезагрузкой страницы, откройте DevTools, перейдите в панель Service Worker на вкладке Application. Это должно выглядеть так.

service worker in application

Когда вы видите пустую страницу, как на примере, это означает, что на текущей открытой странице нет зарегистрированных сервис воркеров.

Перезагрузите страницу. Теперь панель Service Worker должна выглядеть так.

service-worker.js

Когда вы видите такую информацию, это означает, что на странице запущен сервис воркер.

Хорошо, теперь мы собираемся сделать небольшой бриф и продемонстрируем, с чем вы можете столкнуться при разработке сервис воркеров. Чтобы продемонстрировать, добавим в файле service-worker.js  ниже прослушиватель событий install,и activate.

Событие activate запустится при запуске сервис воркера.

Откройте консоль DevTools и перезагрузите страницу, перейдите в панель Service Worker на вкладке Application. Вы ожидаете увидеть в консоли сообщение [ServiceWorker] Activate, но этого не произошло. Проверьте снова панель Service Worker, и вы увидите, что новый сервис воркер (включающий в себя прослушиватель событий activate) находится в состоянии ожидания («waiting»).

waiting to activate

По умолчанию, старый сервис воркер контролирует страницу до тех пор, пока открыта вкладка с этой страницей. Таким образом, вы можете закрыть и повторно открыть страницу или нажать кнопку skipWaiting, но более долгосрочное решение — просто включить флажок Update on Reload в разделе Service Worker в панели DevTools. Когда этот флажок включен, сервис воркер будет обновляться при каждой перезагрузке страницы.

Установите чекбокс update on reload и перезагрузите страницу, чтобы активировать новый сервис воркер.

Примечание: В разделе Service Worker панели Application может возникнуть ошибка, аналогичная приведенной ниже, игнорировать эту ошибку вполне безопасно.

service worker updated

На этом все, что касается проверки и отладки сервис воркеров в DevTools. Чуть позже мы покажем вам еще несколько трюков. Вернемся к созданию вашего приложения.

Давайте разберем прослушиватель событий activate, для включения некоторой логики для обновления кеша. Обновите код как показано ниже.

Этот код гарантирует, что ваш сервис воркер будет обновлять свой кеш всякий раз, когда изменяется какой-либо из файлов оболочки приложения. Чтобы этот код работал, вам нужно увеличивать значение переменной cacheName в верхней части вашего сервис воркер файла.

Последнее утверждение фиксирует крайнее значение (corner-case), о котором вы можете прочитать в (необязательном) информационном поле ниже.

Когда приложение завершено, self.clients.claim()фиксирует крайнее значение (corner-case), в котором приложение не возвращает последние данные. Вы можете воспроизвести это самое крайнее значение (corner-case), раскомментировав приведенную ниже строку и затем сделав следующие шаги: 1) загрузите приложение в первый раз, будут инициализированы и показаны данные города Нью-Йорк 2) нажмите в приложении кнопку обновления 3) выйдете в оффлайн режим 4 ) перезагрузите приложение. Вы ожидаете увидеть новые данные Нью-Йорка, но на самом деле увидите инициализированные данные. Это происходит потому, что сервис воркер еще не активирован. Self.clients.claim() по сути позволяет активировать сервис воркер быстрее.

Наконец, давайте обновим список файлов, необходимых для оболочки приложения. Нам нужно включить в массив все файлы, необходимые нашему приложению, включая изображения, JavaScript, CSS и т. д. В верхней части файла service-worker.js замените var filesToCache = []; кодом ниже:

Убедитесь, что учтены все возможные имена файлов, например, наше приложение запускается из index.html, но его также можно запросить как /, и когда запрашивается корневая папка, сервер отправляет index.html.  Вы можете обработать это в методе fetch, но так это потребовало бы специального преобразования регистра, что может стать сложным.

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

Запуск оболочки приложения из кэша

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

Например:

Давайте теперь загрузим оболочку приложения из кэша. Добавьте следующий код в конец файла service-worker.js:

Caches.match() оценивает веб-запрос, инициировавший событие fetch, и проверяет, доступен ли он в кэше. Затем он либо отвечает кэшированной версией, либо использует fetch для получения копии из сети. Ответ response возвращается на веб-страницу с помощью e.respondWith().

Если вы не видите лог [ServiceWorker] в консоли, убедитесь, что вы изменили переменную cacheName и что вы инспектируете правильный сервис воркер, открыв раздел Service Worker на панели Applications и нажав кнопку inspect на запущенном сервис воркере. Если это не сработает, см. раздел «Советы по тестированию живых сервис воркеров».

Тестирование

Теперь ваше приложение оффлайн-совместимо! Давайте проверим.

Перезагрузите страницу и перейдите в раздел Cache Storage на вкладке Application панели DevTools. Щелкните правой кнопкой мыши Cache Storage, выберите Refresh Caches, разверните раздел и чуть ниже вы увидите имя кэша оболочки приложения. Нажав на кэш оболочки приложения, вы можете увидеть все ресурсы, которые он в настоящее время закэшировал.

Cache Storage

Теперь давайте проверим оффлайн режим. Вернитесь на вкладку Service Workerпанели DevTools и установите флажок Offline. После включения вы увидите желтый предупреждающий значок рядом с вкладкой Network. Это означает, что вы в оффлайн режиме.

Offline checkbox

Перезагрузите свою страницу и … она работает! Ну по крайней мере внешне. Обратите внимание, как он загружает исходные (фейковые) данные о погоде.

Fake weather data

Ознакомьтесь с условием else в app.getForecast(), чтобы понять, когда приложение будет загружать фейковые данные.

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

Совет: Чтобы очистить все сохраненные данные (localStorage, базу indexedDB, кэшированные файлы) и удалить все сервис воркеры, используйте Clear storage на вкладке Application.

Остерегайтесь edge case-ов (граничных случаев)

Как уже упоминалось ранее, этот код не должен использоваться в продакшене из-за большого количества необработанных edge case-ов.

Обновление кэша зависит от обновления ключа кэша после каждого изменения

Этот метод кэширования требует, чтобы при каждом изменении контента вы обновляли ключ кеша, иначе кеш не будет обновляться, и будет показываться старый контент. Поэтому при каждом изменении контента не забудьте изменять ключ кеша, когда работаете над своим проектом!

После каждого изменения необходимо, чтобы все было загружено снова 

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

Кэш браузера может препятствовать сервис воркеру обновлять кэш 

Здесь есть еще одно важное предостережение. Крайне важно, чтобы HTTPS запрос, сделанный в течение установки обработчика, сразу попадал в сеть, а не возвращал ответ из кэша браузера. В противном случае браузер может вернуть старую кэшированную версию, в результате чего кэш сервис воркера никогда не обновится!

Остерегайтесь cache-first стратегий в продакшене

Наше приложение использует cache-first стратегию, это значит, что копия любого кэшированного содержимого возвращается без обращения к сети. Хотя стратегию, основанную на кэше, легко реализовать, она может вызвать проблемы в будущем. После того, как закэшируется копия главной страницы и регистрация сервис воркера, может стать чрезвычайно сложно изменить конфигурацию сервис воркера (поскольку конфигурация зависит от того, где она была определена), в итоге вы столкнетесь с тем, что ваши развернутые сайты чрезвычайно сложно обновить!

Как мне избежать этих edge case-ов?

Итак, как мы можем избежать эти edge case-ы? Используйте библиотеку sw-precache, которая обеспечивает прекрасный контроль над подключением к сети, также она гарантирует, что запросы будут идти непосредственно в сеть (при подключении к сети) и выполнит за вас всю тяжелую работу.

Советы по тестированию работающих сервис воркеров

Отладка сервис воркеров может стать испытанием, и может стать еще большим кошмаром при отладке кэша, который не обновляется, когда вы этого от него ожидаете. Одна ошибка — и все пойдет прахом. Но не стоит беспокоиться. Существуют несколько инструментов, используя которые вы упростите себе жизнь.

Очистка сохраненных данных

Иногда вы будете сталкиваться с необходимостью обновить кэшированные данные. Чтобы очистить все сохраненные данные (localStorage, indexedDB данные, кэшированные файлы) и удалить все сервис воркеры, используйте раздел Clear storage на вкладке Application.

Некоторые советы:

  • После того, как сервис воркер стал незарегистрированым, ссылка на него сохраняется и он может продолжать работать до тех пор, пока не будет закрыто окно браузера где он содержится.
  • Если открыть несколько окон для вашего приложения, новый сервис воркер не вступит в силу до тех пор, пока все они не будут перезагружены и обновлены до последнего сервис воркера.
  • Отмена регистрации сервис воркера не очищает кэш, поэтому возможно, вы будете получать старые данные, пока имя кэша не изменится.
  • Если зарегистрировать новый сервис воркер и при этом уже существует какой-либо сервис воркер, новый сервис воркер не будет получать контроль до тех пор, пока страница не будет перезагружена, если конечно вы не используете immediate control.
 

7. Используем service workers для кэширования прогнозных данных

Выбор правильной caching strategy для ваших данных жизненно важен и зависит от типа данных, которые представляет ваше приложение. Например, данные, зависящие от времени, такие как погода или котировки акций, должны быть как можно более свежими, в то время как изображения или содержимое статьи могут обновляться не так часто.

Стратегия cache-first-then-network идеально подходит для нашего приложения. Приложение выводит данные на экран как можно быстрее, а затем обновляет, как только из сети загрузятся последние данные. В сравнении с network-first-then-cache, пользователю не нужно ждать, пока по таймауту закончится fetch и не будут получены кэшированные данные.

Cache-first-then-network означает, что нам нужно запустить два асинхронных запроса, один — в кэш и один в сеть. Наш сетевой запрос из приложения не требует значительных изменений, но нам нужно изменить сервис воркер для кэширования ответа перед его выводом.

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

Перехват сетевого запроса и кэширование ответа

Нам нужно изменить сервис воркер, чтобы перехватывать запросы к API погоды и сохранять ответы в кэше, чтобы позже мы могли легко получить к ним доступ. В стратегии cache-then-network мы ожидаем, что сетевой ответ станет ‘source of truth’ («источником правды»), всегда предоставляя нам самую свежую информацию. Если ответ не будет получен, ничего страшного, потому что мы уже извлекли последние кэшированные данные в наше приложении.

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

Добавьте следующую строку в начало файла service-worker.js:

Затем обновите обработчик события activate, чтобы он не удалял кеш данных, когда он очищает кеш оболочки приложения.

Наконец, обновите обработчик события fetch для обработки запросов к API отдельно от других запросов.

Код перехватывает запрос и проверяет, начинается ли URL с адреса API погоды. Если да, то мы будем использовать для запроса fetch. Как только ответ возвращается, наш код открывает кэш, клонирует ответ, сохраняет его в кэше и, наконец, возвращает ответ обратно.

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

Выполнение запросов

Как уже упоминалось ранее, приложение должно запускать два асинхронных запроса, один для кэша и один для сети. Для доступа к кэшу и получения последних данных приложение использует объект caches доступный в window. Это отличный пример прогрессивного улучшения, поскольку объект caches может быть доступен не во всех браузерах, или если не доступен сетевой запрос, приложение все равно будет работать.

Для всего этого нам необходимо:

  1. Проверить, доступен ли объект caches в глобальном объекте window.
  2. Запросить данные из кеша.
    • Если запрос сервера по-прежнему неактивен, обновить приложение с помощью кэшированных данных.
  3. Запросить данные с сервера.
    • Сохранить данные для быстрого доступа позже.
    • Обновить приложение со свежими данными от сервера.

Получение данных из кеша

Следующим шагом нам нужно проверить, существует ли объект caches и запросить у него последние данные. Найдите комментарий TODO add cache logic here в app.getForecas(), и добавьте код ниже комментария.

Теперь наше погодное приложение делает два асинхронных запроса для получения данных, один из cache и один через XHR. Если в кеше имеются данные, они будут возвращены и отображены очень быстро (за десятки миллисекунд), но если XHR все еще выполняется. Затем, когда XHR вернет ответ, карточка с погодой будет обновлена ​​самыми свежими данными непосредственно из API погоды.

Обратите внимание на то, как запрос кэша и запрос XHR заканчиваются вызовом для обновления карточки прогноза. Откуда приложение знает, отображает ли он последние данные? Это описано в следующем коде из app.updateForecastCard:

Каждый раз, при обновлении карточки, приложение сохраняет временную метку данных в скрытом атрибуте на карточке. Если временная метка, которая уже существует на карточке, новее, чем данные, переданные функции, приложение просто не обновляется.

Тестирование

Теперь приложение должно быть полностью автономным. Чтобы получить свежие данные о погоде сохраните несколько городов и нажмите в приложении кнопку обновления, затем перейдите в оффлайн режим и перезагрузите страницу.

Теперь перейдите в раздел Cache Storage на панели Application DevTools. Разверните секцию, и вы увидите имя вашей оболочки приложения и кеш данных, перечисленных чуть ниже слева. В данных кеша должна храниться информация по каждому городу.

Cache Storage

 

8. Поддержка нативной интеграции

Никто не любит печатать длинные URL-адреса на мобильной клавиатуре. С помощью функции  Add To home screen ваши пользователи могут добавить ярлык на домашний экран, как если бы они устанавливали нативное приложение из магазина, но с гораздо меньшим трением.

Web App Install Banners и Add to Homescreen для Chrome на Android

Web app install banners позволяют пользователям быстро и легко добавлять веб-приложение на свой домашний экран, что упрощает запуск и возврат в приложение. Добавить app install banners легко, и Chrome выполнит для вас большую часть тяжелой работы. Нам просто нужно включить файл манифеста веб-приложения с подробностями о приложении.

Затем Chrome будет использовать набор критериев, включая использование сервис воркера, статус SSL и частоту посещения, чтобы определить, когда показывать баннер. Кроме того, пользователь может вручную добавить его с помощью кнопки меню Add to Home Screen в Chrome.

Объявление манифеста приложения с помощью файла 

manifest.json

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

Используя манифест веб-приложения вы сможете:

  • Улучшить отображение на домашнем экране пользователя Android
  • Запускать приложение на Android в полноэкранном режиме без адресной строки
  • Управлять ориентацией экрана для оптимального просмотра
  • Определять «splash screen» при запуске приложения и цвет темы сайта
  • Отслеживать запущено ли вы приложение с домашнего экрана или из адресной строки

Создайте файл с именем manifest.json в рабочей папке work, скопируйте и вставьте следующее содержимое:

Манифест поддерживает массив иконок, предназначенных для различных размеров экрана. На момент написания этой статьи, Chrome и Opera Mobile — единственные браузеры, которые поддерживают манифесты веб-приложения, не использующие что-либо меньше, чем 192px.

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

Сообщаем браузеру о вашем manifest файле

Теперь добавьте следующую строку в нижнюю часть элемента <head> в файле index.html:

Лучшие практики:

  • Поместите ссылку манифеста на все страницы вашего сайта, так чтобы Chrome извлекал его, независимо от того, на какую страницу приземляется пользователь.
  • Short_name является приоритетным для Chrome и будет использоваться он, если он присутствует, а не поле с именем.
  • Определите набор иконок для различной пиксельной плотности экранов. Chrome будет пытаться использовать иконку кратную 48dp, например, 96px на устройствах 2x или 144px для 3х устройств.
  • Не забывайте использовать иконки размером, который подходит для сплеш скрина и не забудьте установить background_color.

Подробнее тут:

Использование app install banners

Add to Homescreen элемент для Safari на iOS

В конце элемента <head> в вашем index.html добавьте следующее:

Tile Иконка для Windows

В конце элемента <head> в вашем index.html добавьте следующее:

Тестирование

В этом разделе мы покажем вам пару способов протестировать манифест веб-приложения.

Первый способ — DevTools. Откройте раздел Manifest на панели Application. Если в манифесте вы все написали правильно, вы сможете увидеть, как он анализируется и отображается в удобном для пользователя формате.

В этой панели вы также можете протестировать функцию add to homescreen. Нажмите кнопку Add to homescreen. Вы должны увидеть сообщение add this site to your shelf чуть ниже строки с URL адресом, как показано на скриншоте ниже.

Add to homescreen

Это десктопный вариант мобильной функции add to homescreen. Если вы можете успешно выполнить эту всплывашку на десктопе, вы можете быть уверены, что мобильные пользователи смогут добавить ваше приложение на свои устройства.

Второй способ тестирования — через Web Server for Chrome. При таком подходе вы открываете свой локальный сервер разработчика (на десктопе или ноутбуке) для других компьютеров, а затем просто подключаетесь к вашему прогрессивному веб-приложению с реального мобильного устройства.

Открытие порта для удаленного доступа удобно для целей тестирования, но может быть заблокировано настройками брандмауэра на вашем компьютере или файрволом. Открытие портов для удаленного доступа, как правило, не лучшая идея. Таким образом, по соображениям безопасности, когда вы завершите тестирование, отключите параметр Accessible on local network и перезапустите веб-сервер.
В диалоговом окне конфигурации Web Server for Chrome выберите параметр Accessible on local network:
Accessible on local network
 

Переключите веб-сервер в STOPPED и вернитесь обратно в STARTED. Вы увидите новый URL-адрес, который можно использовать для удаленного доступа к вашему приложению.

Теперь, используя новый URL, перейдите на свой сайт с мобильного устройства.

При тестировании вы увидите в консоли ошибки сервис воркера, потому что сервис воркер не обслуживается через HTTPS.

Используя Chrome в Android устройстве, попробуйте добавить приложение на рабочий стол и убедитесь, что окошко запуска отображается правильно, и используются правильные иконки.

Точно так же можно добавить приложение на свой рабочий стол в Safari и Internet Explorer.

9. Развертываем на безопасном хосте и пляшем

Последний шаг — развернуть наше погодное приложение на сервер, поддерживающий HTTPS. Если у вас его еще нет, самым простым (и бесплатным) решением является использование статического контента с Firebase. Он очень прост в использовании, обслуживает контент через HTTPS и поддерживается глобальным CDN.

Дополнительная информация: минификация и встроенный CSS

Есть еще одна вещь, которую вы должны учитывать, минимизируя ключевые стили и вставляя их непосредственно в index.html. Page Speed ​​Insights рекомендует использовать не более 15k байт в первом запросе.

Посмотрите, насколько маленьким можно сделать первоначальный запрос.

Подробнее тут: PageSpeed Insight правила

Этот шаг требует, чтобы в вашей системе был установлен Node & NPM. Если нет, тогда вы можете использовать любой другой хостинг-провайдер, поддерживающий HTTPS. Мы использовали Firebase, потому что он автоматически перенаправляет пользователей с HTTP на HTTPS. Если вы используете другого провайдера, убедитесь, что он всегда перенаправляет на HTTPS.

Развертывание на Firebase

Если вы новичок в Firebase, то сперва вам нужно создать свою учетную запись и установить некоторые инструменты.

  1. Создайте учетную запись Firebase на https://firebase.google.com/console/
  2. Установите Firebase инструменты через npm: npm install -g firebase-tools

Как только вы создали учетную запись и вошли в систему, вы готовы к развертыванию!

  1. Создайте новое приложение на странице https://firebase.google.com/console/
  2. Если вы не вошли в инструменты Firebase, обновите свои учетные данные: firebase login
  3. Инициализируйте свое приложение и укажите каталог (например work), где будет ваше готовое приложение: firebase init
  4. Наконец, разверните приложение на Firebase: firebase deploy
  5. Пляшите. Все готово! Ваше приложение будет развернуто в домене: https://YOUR-FIREBASE-APP.firebaseapp.com

Подробнее тут: Firebase Hosting Guide

Тестирование

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

Не совсем точный перевод кодлаба на codelabs.developers.google.com