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

Последнее обновление: 2019-04-30

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

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

Быстро и надежно

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

И он должен быть надежно быстрым . Трудно достаточно подчеркнуть, насколько лучше надежная производительность. Подумайте об этом так: первая загрузка нативного приложения разочаровывает. Это закрыто магазином приложений и огромным количеством загрузок, но как только вы дойдете до точки, где приложение установлено, эта первоначальная стоимость амортизируется при всех запусках приложения, и ни один из этих запусков не имеет переменной задержки. Каждое приложение запускается так же быстро, как и предыдущее, без отклонений. Прогрессивное веб-приложение должно обеспечивать надежную работу, которую пользователи ожидают от любого установленного приложения.

Устанавливаемый

Прогрессивные веб-приложения могут работать на вкладке браузера, но их также можно установить. Добавление сайта в закладки просто добавляет ярлык, но установленное прогрессивное веб-приложение (PWA) выглядит и ведет себя так же, как и все другие установленные приложения. Он запускается из того же места, что и другие приложения. Вы можете управлять процессом запуска, включая настраиваемый экран-заставку, значки и многое другое. Он работает как приложение в окне приложения без адресной строки или другого пользовательского интерфейса браузера. Как и все другие установленные приложения, это приложение верхнего уровня в переключателе задач.

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

Мобильный и рабочий стол

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

Что вы будете строить

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

  • Используйте адаптивный дизайн, чтобы он работал на компьютере или мобильном устройстве.
  • Работайте быстро, используя сервис-воркер для предварительного кэширования ресурсов приложения (HTML, CSS, JavaScript, изображений), необходимых для запуска, и для кэширования данных о погоде во время выполнения для повышения производительности.
  • Быть устанавливаемым, используя манифест веб-приложения и событие beforeinstallprompt , чтобы уведомить пользователя о возможности установки.

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

  • Как создать и добавить манифест веб-приложения
  • Как обеспечить простой офлайн-опыт
  • Как обеспечить полноценный офлайн-опыт
  • Как сделать ваше приложение устанавливаемым

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

Что вам понадобится

  • Последняя версия Chrome (74 или более поздняя). PWA работают во всех браузерах, но мы будем использовать некоторые функции Chrome DevTools, чтобы лучше понять, что происходит на уровне браузера, и использовать их для тестирования процесса установки.
  • Знание HTML, CSS, JavaScript и Chrome DevTools .

Получить ключ для Dark Sky API

Наши данные о погоде поступают из Dark Sky API . Чтобы использовать его, вам необходимо запросить ключ API. Он прост в использовании и бесплатен для некоммерческих проектов.

Зарегистрируйтесь для API-ключа

Убедитесь, что ваш ключ API работает правильно

Чтобы проверить правильность работы вашего ключа API, сделайте HTTP-запрос к API DarkSky. Обновите приведенный ниже URL-адрес, заменив DARKSKY_API_KEY своим ключом API. Если все работает, вы должны увидеть последний прогноз погоды для Нью-Йорка.

https://api.darksky.net/forecast/DARKSKY_API_KEY/40.7720232,-73.9732319

Получить код

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

Настоятельно рекомендуется: используйте Glitch для импорта репо.

Использование Glitch является рекомендуемым методом для работы с этой кодовой лабораторией.

  1. Откройте новую вкладку браузера и перейдите на https://glitch.com .
  2. Если у вас нет учетной записи, вам необходимо зарегистрироваться.
  3. Нажмите «Новый проект» , затем « Клонировать из репозитория Git».
  4. Клонируйте https://github.com/googlecodelabs/your-first-pwapp.git и нажмите OK.
  5. После загрузки репозитория отредактируйте файл .env и обновите его с помощью ключа DarkSky API.
  6. Нажмите кнопку « Показать» , затем выберите «В новом окне », чтобы увидеть PWA в действии.

Альтернатива: загрузите код и работайте локально

Если вы хотите загрузить код и работать локально, вам потребуется последняя версия Node.js и настроенный и готовый к работе редактор кода.

Скачать исходный код

  1. Распакуйте загруженный zip-файл.
  2. Запустите npm install , чтобы установить зависимости, необходимые для запуска сервера.
  3. Отредактируйте server.js и установите ключ API DarkSky.
  4. Запустите node server.js , чтобы запустить сервер на порту 8000.
  5. Откройте вкладку браузера по адресу http://localhost:8000.

Какова наша отправная точка?

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

Некоторые вещи, чтобы попробовать...

  1. Добавьте новый город с помощью синей кнопки + в правом нижнем углу.
  2. Обновите данные с помощью кнопки обновления в правом верхнем углу.
  3. Удалите город, используя значок x в правом верхнем углу каждой карты города.
  4. Используя панель инструментов переключения устройств в Chrome DevTools, посмотрите, как она работает на настольных и мобильных устройствах.
  5. Используя панель « Сеть » в Chrome DevTools, посмотрите, что происходит, когда вы выходите из сети.
  6. Используя панель « Сеть » в Chrome DevTools, посмотрите, что происходит, когда сеть переключается на «Медленный 3G».
  7. Добавьте задержку на сервер прогнозов, изменив значение FORECAST_DELAY в server.js

Аудит с Lighthouse

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

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

Запустим Маяк

  1. Откройте свой проект в новой вкладке.
  2. Откройте Chrome DevTools и переключитесь на панель Audits . Оставьте все типы аудита включенными.
  3. Щелкните Провести аудит . Через некоторое время Lighthouse выдает вам отчет на странице.

Аудит прогрессивного веб-приложения

Мы сосредоточимся на результатах аудита Progressive Web App.

И есть много красного, на котором стоит сосредоточиться:

  • ❗FAILED: Текущая страница не отвечает кодом 200 в автономном режиме.
  • ❗FAILED: start_url не отвечает кодом 200 в автономном режиме.
  • ❗FAILED: не регистрируется сервис-воркер, управляющий страницей и start_url.
  • ❗FAILED: манифест веб-приложения не соответствует требованиям установки.
  • ❗FAILED: не настроен для пользовательского экрана-заставки.
  • ❗FAILED: не устанавливает цвет темы адресной строки.

Давайте прыгнем и начнем исправлять некоторые из этих проблем!

К концу этого раздела наше погодное приложение пройдет следующие проверки:

  • Манифест веб-приложения не соответствует требованиям установки.
  • Не настроен для пользовательского экрана-заставки.
  • Не устанавливает цвет темы адресной строки.

Создайте манифест веб-приложения

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

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

  • Сообщите браузеру, что вы хотите, чтобы ваше приложение открывалось в отдельном окне ( display ).
  • Определите, какая страница открывается при первом запуске приложения ( start_url ).
  • Определите, как должно выглядеть приложение в доке или в панели запуска приложений ( short_name , icons ).
  • Создайте заставку ( name , icons , colors ).
  • Скажите браузеру открыть окно в ландшафтном или портретном режиме ( orientation ).
  • И многое другое .

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

public/manifest.json

{
  "name": "Weather",
  "short_name": "Weather",
  "icons": [{
    "src": "/images/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    }, {
      "src": "/images/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    }, {
      "src": "/images/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    }, {
      "src": "/images/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    }, {
      "src": "/images/icons/icon-256x256.png",
      "sizes": "256x256",
      "type": "image/png"
    }, {
      "src": "/images/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }],
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#3E4EB8",
  "theme_color": "#2F3BA2"
}

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

Затем нам нужно сообщить браузеру о нашем манифесте, добавив <link rel="manifest"... на каждую страницу нашего приложения. Добавьте следующую строку в элемент <head> в файле index.html .

общедоступный/index.html

<!-- CODELAB: Add link rel manifest -->
<link rel="manifest" href="/manifest.json">

Объезд инструментов разработчика

DevTools предоставляет быстрый и простой способ проверить файл manifest.json . Откройте панель манифеста на панели приложения . Если вы правильно добавили информацию манифеста, вы сможете увидеть ее проанализированную и отображенную в удобном для человека формате на этой панели.

Добавьте метатеги и значки iOS

Safari на iOS не поддерживает манифест веб-приложения ( пока ), поэтому вам нужно будет добавить традиционные meta в <head> вашего файла index.html :

общедоступный/index.html

<!-- CODELAB: Add iOS meta tags and icons -->
<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="Weather PWA">
<link rel="apple-touch-icon" href="/images/icons/icon-152x152.png">

Бонус: исправления Easy Lighthouse

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

Установить мета-описание

В ходе SEO-аудита компания Lighthouse отметила, что наш « Документ не имеет метаописания». Описания могут отображаться в результатах поиска Google. Качественные уникальные описания могут сделать ваши результаты более релевантными для поисковых пользователей и увеличить поисковый трафик.

Чтобы добавить описание, добавьте следующий meta в <head> вашего документа:

общедоступный/index.html

<!-- CODELAB: Add description here -->
<meta name="description" content="A sample weather app">

Установить цвет темы адресной строки

В ходе аудита PWA компания Lighthouse отметила, что наше приложение « не устанавливает цвет темы адресной строки ». Тематика адресной строки браузера, соответствующая цветам вашего бренда, обеспечивает более захватывающий пользовательский интерфейс.

Чтобы установить цвет темы на мобильном устройстве, добавьте следующий meta в <head> вашего документа:

общедоступный/index.html

<!-- CODELAB: Add meta theme-color -->
<meta name="theme-color" content="#2F3BA2" />

Подтвердите изменения с помощью Lighthouse

Запустите Lighthouse еще раз (нажав значок + в верхнем левом углу панели «Аудит») и проверьте свои изменения.

SEO-аудит

  • ✅ ПРОЙДЕНО: Документ имеет метаописание.

Прогрессивный аудит веб-приложений

  • ❗FAILED: Текущая страница не отвечает кодом 200 в автономном режиме.
  • ❗FAILED: start_url не отвечает кодом 200 в автономном режиме.
  • ❗FAILED: не регистрируется сервис-воркер, управляющий страницей и start_url.
  • ✅ ПРОЙДЕНО: манифест веб-приложения соответствует требованиям установки.
  • ✅ ПРОЙДЕНО: настроен для пользовательского экрана-заставки.
  • ✅ ПРОЙДЕНО: устанавливает цвет темы адресной строки.

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

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

  • Текущая страница не отвечает кодом 200 в автономном режиме.
  • start_url не отвечает 200 в автономном режиме.
  • Не регистрирует работника службы, который контролирует страницу и start_url.

В следующем разделе мы заменим нашу настраиваемую офлайн-страницу на полнофункциональную офлайн-страницу. Это улучшит работу в автономном режиме, но, что более важно, значительно улучшит нашу производительность, поскольку большая часть наших ресурсов (HTML, CSS и JavaScript) будет храниться и обслуживаться локально, что устраняет потенциальные узкие места в сети.

Служащие спешат на помощь

Если вы не знакомы с сервис-воркерами, вы можете получить общее представление о том, что они могут делать, как работает их жизненный цикл и многое другое, прочитав Introduction To Service Workers .

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

Зарегистрируйте сервис-воркер

Первым шагом является регистрация сервис-воркера. Добавьте следующий код в файл index.html :

общедоступный/index.html

// CODELAB: Register service worker.
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
        .then((reg) => {
          console.log('Service worker registered.', reg);
        });
  });
}

Этот код проверяет, доступен ли API сервис-воркера, и если да, сервис-воркер в /service-worker.js регистрируется после загрузки страницы.

Обратите внимание, что сервис-воркер обслуживается из корневого каталога, а не из каталога /scripts/ . Это самый простой способ установить scope действия вашего сервис-воркера. Область scope сервис-воркера определяет, какие файлы он контролирует, другими словами, по какому пути сервис-воркер будет перехватывать запросы. Областью scope по умолчанию является расположение файла сервис-воркера, и она распространяется на все каталоги ниже. Таким образом, если service-worker.js находится в корневом каталоге, Service Worker будет контролировать запросы со всех веб-страниц в этом домене.

Предварительно кэшировать автономную страницу

Во-первых, нам нужно сообщить сервис-воркеру, что кэшировать. Мы уже создали простую автономную страницу ( public/offline.html ), которую будем отображать в любое время, когда нет подключения к сети.

В вашем service-worker.js добавьте '/offline.html', в массив FILES_TO_CACHE , окончательный результат должен выглядеть так:

общественность/service-worker.js

// CODELAB: Add list of files to cache here.
const FILES_TO_CACHE = [
  '/offline.html',
];

Затем нам нужно добавить следующий код в событие install , чтобы указать работнику службы предварительно кэшировать автономную страницу:

общественность/service-worker.js

// CODELAB: Precache static resources here.
evt.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      console.log('[ServiceWorker] Pre-caching offline page');
      return cache.addAll(FILES_TO_CACHE);
    })
);

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

Когда кеш открыт, мы можем вызвать cache.addAll() , который берет список URL-адресов, извлекает их с сервера и добавляет ответ в кеш. Обратите внимание, что cache.addAll() завершается ошибкой, если какой-либо из отдельных запросов завершается ошибкой. Это означает, что вам гарантировано, что если шаг установки завершится успешно, ваш кеш будет в согласованном состоянии. Но, если по какой-то причине это не удается, он автоматически попытается снова при следующем запуске работника службы.

Объезд инструментов разработчика

Давайте посмотрим, как вы можете использовать DevTools для понимания и отладки рабочих процессов. Перед перезагрузкой страницы откройте DevTools и перейдите на панель Service Workers на панели Application . Это должно выглядеть так:

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

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

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

Рядом с меткой состояния есть число (в данном случае 34251 ). Следите за этим числом, когда работаете с сервисными работниками. Это простой способ узнать, был ли обновлен ваш сервис-воркер.

Очистка старых офлайн-страниц

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

Добавьте следующий код в событие activate :

общественность/service-worker.js

// CODELAB: Remove previous cached data from disk.
evt.waitUntil(
    caches.keys().then((keyList) => {
      return Promise.all(keyList.map((key) => {
        if (key !== CACHE_NAME) {
          console.log('[ServiceWorker] Removing old cache', key);
          return caches.delete(key);
        }
      }));
    })
);

Объезд инструментов разработчика

Открыв панель Service Workers, обновите страницу. Вы увидите, что новый сервис-воркер установлен, а номер состояния увеличен.

Обновленный сервис-воркер немедленно получает управление, потому что наше событие install завершается с помощью self.skipWaiting() , а событие activate завершается с помощью self.clients.claim() . Без них старый сервис-воркер продолжал бы контролировать страницу, пока на странице открыта вкладка.

Обработка неудачных сетевых запросов

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

общественность/service-worker.js

// CODELAB: Add fetch event handler here.
if (evt.request.mode !== 'navigate') {
  // Not a page navigation, bail.
  return;
}
evt.respondWith(
    fetch(evt.request)
        .catch(() => {
          return caches.open(CACHE_NAME)
              .then((cache) => {
                return cache.match('offline.html');
              });
        })
);

Обработчик fetch должен обрабатывать только переходы по страницам, поэтому другие запросы могут быть выгружены из обработчика и нормально обработаны браузером. Но если запрос .mode имеет значение navigate , используйте fetch , чтобы попытаться получить элемент из сети. Если это не удается, обработчик catch открывает кеш с помощью caches.open(CACHE_NAME) и использует cache.match('offline.html') для получения предварительно кэшированной автономной страницы. Затем результат передается обратно в браузер с помощью evt.respondWith() .

Объезд инструментов разработчика

Давайте проверим, чтобы убедиться, что все работает так, как мы ожидаем. Открыв панель Service Workers , обновите страницу. Вы увидите, что новый сервис-воркер установлен, а номер состояния увеличен.

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

Теперь давайте проверим автономный режим. Вернитесь на панель Service Workers на панели Application в DevTools и установите флажок Offline . После проверки вы должны увидеть маленький желтый значок предупреждения рядом с вкладкой панели « Сеть ». Этот значок означает, что вы не в сети.

Перезагрузите страницу и... все работает! Мы получаем нашу офлайн-панду вместо офлайн-динозавра Chrome!

Советы по тестированию сервис-воркеров

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

Используйте инструменты разработчика

На панели Service Workers панели Application есть несколько флажков, которые сделают вашу жизнь намного проще.

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

Начать заново

В некоторых случаях вы можете обнаружить, что загружаете кэшированные данные или что-то не обновляется так, как вы ожидаете. Чтобы очистить все сохраненные данные (localStorage, данные indexedDB, кэшированные файлы) и удалить всех сервис-воркеров, используйте панель « Очистить хранилище » на панели « Приложение ». Кроме того, вы также можете работать в окне инкогнито.

Дополнительные советы:

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

Подтвердите изменения с помощью Lighthouse

Запустите Lighthouse еще раз и проверьте свои изменения. Не забудьте снять флажок «Не в сети» перед проверкой изменений!

SEO-аудит

  • ✅ ПРОЙДЕНО: Документ имеет метаописание.

Прогрессивный аудит веб-приложений

  • ✅ ПРОШЕЛ: текущая страница отвечает кодом 200 в автономном режиме.
  • ✅ ПРОЙДЕНО: start_url отвечает 200 в автономном режиме.
  • ✅ ПРОШЕЛ: регистрирует сервис-воркер, который контролирует страницу и start_url.
  • ✅ ПРОЙДЕНО: манифест веб-приложения соответствует требованиям установки.
  • ✅ ПРОЙДЕНО: настроен для пользовательского экрана-заставки.
  • ✅ ПРОЙДЕНО: устанавливает цвет темы адресной строки.

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

Жизненный цикл сервисного работника

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

install событие

Первым событием, которое получает сервис-воркер, является install . Он запускается, как только рабочий процесс выполняется, и вызывается только один раз для каждого сервисного работника. Если вы измените свой скрипт сервис-воркера, браузер сочтет его другим сервис-воркером и получит собственное событие install .

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

activate событие

Сервисный работник будет получать событие activate при каждом запуске. Основная цель события activate — настроить поведение сервис-воркера, очистить все ресурсы, оставшиеся после предыдущих запусков (например, старые кеши), и подготовить сервис-воркер к обработке сетевых запросов (например, событие fetch , описанное ниже).

fetch событие

Событие fetch позволяет сервис-воркеру перехватывать любые сетевые запросы и обрабатывать запросы. Он может обратиться в сеть, чтобы получить ресурс, он может извлечь его из собственного кеша, сгенерировать пользовательский ответ или использовать любое количество различных вариантов. Ознакомьтесь с Offline Cookbook , чтобы узнать о различных стратегиях, которые вы можете использовать.

Обновление сервис-воркера

Браузер проверяет наличие новой версии сервисного работника при каждой загрузке страницы. Если он находит новую версию, новая версия загружается и устанавливается в фоновом режиме, но не активируется. Новая версия вашего сервис-воркера находится в состоянии ожидания, пока не перестанут открываться страницы, использующие старый сервис-воркер. Как только все окна, использующие старый сервис-воркер, закрыты, новый сервис-воркер активируется и может взять на себя управление. Дополнительную информацию см. в разделе « Обновление сервисного работника » документа «Жизненный цикл сервисного работника».

Выбор правильной стратегии кэширования

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

Кэширование статических ресурсов

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

Для нашего приложения мы предварительно кэшируем все наши статические ресурсы, когда наш сервис-воркер установлен, чтобы все, что нам нужно для запуска нашего приложения, сохранялось на устройстве пользователя. Чтобы обеспечить молниеносную загрузку нашего приложения, мы будем использовать стратегию « Cache-first »; вместо того, чтобы обращаться к сети за ресурсами, они извлекаются из локального кеша; только если он недоступен, мы попытаемся получить его из сети.

Извлечение из локального кеша устраняет любую изменчивость сети. Независимо от того, в какой сети находится пользователь (WiFi, 5G, 3G или даже 2G), ключевые ресурсы, необходимые для работы, доступны почти сразу.

Кэширование данных приложения

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

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

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

Обновить логику приложения

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

Обновите getForecastFromCache() , чтобы проверить, доступен ли объект caches в объекте глобального window , и если да, то запросить данные из кеша.

общедоступные/скрипты/app.js

// CODELAB: Add code to get weather forecast from the caches object.
if (!('caches' in window)) {
  return null;
}
const url = `${window.location.origin}/forecast/${coords}`;
return caches.match(url)
    .then((response) => {
      if (response) {
        return response.json();
      }
      return null;
    })
    .catch((err) => {
      console.error('Error getting data from cache', err);
      return null;
    });

Затем нам нужно изменить updateData() так, чтобы он выполнял два вызова: один для getForecastFromNetwork() для получения прогноза из сети и один для getForecastFromCache() для получения последнего кэшированного прогноза:

общедоступные/скрипты/app.js

// CODELAB: Add code to call getForecastFromCache.
getForecastFromCache(location.geo)
    .then((forecast) => {
      renderForecast(card, forecast);
    });

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

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

общедоступные/скрипты/app.js

// If the data on the element is newer, skip the update.
if (lastUpdated >= data.currently.time) {
  return;
}

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

Предварительно кэшируйте ресурсы нашего приложения

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

общественность/service-worker.js

// CODELAB: Update cache names any time any of the cached files change.
const CACHE_NAME = 'static-cache-v2';
const DATA_CACHE_NAME = 'data-cache-v1';

Не забудьте также обновить CACHE_NAME ; мы также изменим все наши статические ресурсы.

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

Обновите массив FILES_TO_CACHE со списком файлов:

общественность/service-worker.js

// CODELAB: Add list of files to cache here.
const FILES_TO_CACHE = [
  '/',
  '/index.html',
  '/scripts/app.js',
  '/scripts/install.js',
  '/scripts/luxon-1.11.4.js',
  '/styles/inline.css',
  '/images/add.svg',
  '/images/clear-day.svg',
  '/images/clear-night.svg',
  '/images/cloudy.svg',
  '/images/fog.svg',
  '/images/hail.svg',
  '/images/install.svg',
  '/images/partly-cloudy-day.svg',
  '/images/partly-cloudy-night.svg',
  '/images/rain.svg',
  '/images/refresh.svg',
  '/images/sleet.svg',
  '/images/snow.svg',
  '/images/thunderstorm.svg',
  '/images/tornado.svg',
  '/images/wind.svg',
];

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

Обновите обработчик события активации

Чтобы гарантировать, что наше событие activate не удалит наши данные случайно, в событии activate service-worker.js замените if (key !== CACHE_NAME) { на:

общественность/service-worker.js

if (key !== CACHE_NAME && key !== DATA_CACHE_NAME) {

Обновите обработчик события выборки

Нам нужно изменить сервис-воркер, чтобы он перехватывал запросы к API погоды и сохранял их ответы в кеше, чтобы мы могли легко получить к ним доступ позже. В стратегии устаревания во время повторной проверки мы ожидаем, что ответ сети будет «источником правды», всегда предоставляя нам самую свежую информацию. If the network can't, it's OK to fail because we've already retrieved the latest cached data in our app.

Update the fetch event handler to handle requests to the data API separately from other requests.

public/service-worker.js

// CODELAB: Add fetch event handler here.
if (evt.request.url.includes('/forecast/')) {
  console.log('[Service Worker] Fetch (data)', evt.request.url);
  evt.respondWith(
      caches.open(DATA_CACHE_NAME).then((cache) => {
        return fetch(evt.request)
            .then((response) => {
              // If the response was good, clone it and store it in the cache.
              if (response.status === 200) {
                cache.put(evt.request.url, response.clone());
              }
              return response;
            }).catch((err) => {
              // Network request failed, try to get it from the cache.
              return cache.match(evt.request);
            });
      }));
  return;
}
evt.respondWith(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.match(evt.request)
          .then((response) => {
            return response || fetch(evt.request);
          });
    })
);

The code intercepts the request and checks if it is for a weather forecast. If it is, use fetch to make the request. Once the response is returned, open the cache, clone the response, store it in the cache, and return the response to the original requestor.

We need to remove the evt.request.mode !== 'navigate' check because we want our service worker to handle all requests (including images, scripts, CSS files, etc), not just navigations. If we left that check in, only the HTML would be served from the service worker cache. Everything else would be requested from the network.

Try it out

The app should be completely offline-functional now. Refresh the page to ensure that you've got the latest service worker installed. Then save a couple of cities and press the refresh button on the app to get fresh weather data.

Next, go to the Cache Storage pane on the Application panel of DevTools. Expand the section and you should see the name of your static cache and data cache listed on the left-hand side. Opening the data cache should show the data stored for each city.

Switch to the Service Workers pane, and check the Offline checkbox. Try reloading the page and then go offline and reload the page.

If you're on a fast network and want to see how weather forecast data is updated on a slow connection, set the FORECAST_DELAY property in server.js to 5000 . All requests to the forecast API will be delayed by 5000ms.

Verify changes with Lighthouse

It's also a good idea to run Lighthouse again.

SEO Audit

  • ✅ PASSED: Document has a meta description.

Progressive Web App Audit

  • ✅ PASSED: Current page responds with a 200 when offline.
  • ✅ PASSED: start_url responds with a 200 when offline.
  • ✅ PASSED: Registers a service worker that controls page and start_url.
  • ✅ PASSED: Web app manifest meets the installability requirements.
  • ✅ PASSED: Configured for a custom splash screen.
  • ✅ PASSED: Sets an address-bar theme color.

When a Progressive Web App is installed, it looks and behaves like all of the other installed apps. It launches from the same place that other apps launch. It runs in an app without an address bar or other browser UI. And like all other installed apps, it's a top level app in the task switcher.

In Chrome, a Progressive Web App can either be installed through the three-dot context menu, or you can provide a button or other UI component to the user that will prompt them to install your app.

Audit with Lighthouse

In order for a user to be able to install your Progressive Web App, the app needs to meet certain criteria . The easiest way to check is to use Lighthouse and make sure it meets the installable criteria.

If you've worked through this codelab, your PWA should already meet these criteria.

Add install.js to index.html

First, let's add the install.js to our index.html file.

public/index.html

<!-- CODELAB: Add the install script here -->
<script src="/scripts/install.js"></script>

Listen for beforeinstallprompt event

If the add to home screen criteria are met, Chrome will fire a beforeinstallprompt event that you can use to indicate your app can be 'installed' and then prompt the user to install it. Add the code below to listen for the beforeinstallprompt event:

public/scripts/install.js

// CODELAB: Add event listener for beforeinstallprompt event
window.addEventListener('beforeinstallprompt', saveBeforeInstallPromptEvent);

Save event and show install button

In our saveBeforeInstallPromptEvent function, we'll save a reference to the beforeinstallprompt event so that we can call prompt() on it later and update our UI to show the install button.

public/scripts/install.js

// CODELAB: Add code to save event & show the install button.
deferredInstallPrompt = evt;
installButton.removeAttribute('hidden');

Show the prompt and hide the button

When the user clicks the install button, we need to call .prompt() on the saved beforeinstallprompt event. We also need to hide the install button, because .prompt() can only be called once on each saved event.

public/scripts/install.js

// CODELAB: Add code show install prompt & hide the install button.
deferredInstallPrompt.prompt();
// Hide the install button, it can't be called twice.
evt.srcElement.setAttribute('hidden', true);

Calling .prompt() will show a modal dialog to the user, asking them to add your app to their home screen.

Log the results

You can check to see how the user responded to the install dialog by listening for the promise returned by the userChoice property of the saved beforeinstallprompt event. The promise returns an object with an outcome property after the prompt has shown and the user has responded to it.

public/scripts/install.js

// CODELAB: Log user response to prompt.
deferredInstallPrompt.userChoice
    .then((choice) => {
      if (choice.outcome === 'accepted') {
        console.log('User accepted the A2HS prompt', choice);
      } else {
        console.log('User dismissed the A2HS prompt', choice);
      }
      deferredInstallPrompt = null;
    });

One comment about userChoice : the spec defines it as a property , not a function as you might expect.

Log all install events

In addition to any UI that you add to install your app, users can also install your PWA through other methods, for example Chrome's three-dot menu. To track these events, listen for the appinstalled event.

public/scripts/install.js

// CODELAB: Add event listener for appinstalled event
window.addEventListener('appinstalled', logAppInstalled);

Then, we'll need to update the logAppInstalled function. For this codelab, we'll just use console.log , but in a production app you probably want to log this as an event with your analytics software.

public/scripts/install.js

// CODELAB: Add code to log the event
console.log('Weather App was installed.', evt);

Update the service worker

Don't forget to update the CACHE_NAME in your service-worker.js file since you've made changes to files that are already cached. Enabling the Bypass for network checkbox in the Service Workers pane of the Application panel in DevTools will work in development, but won't help in the real world.

Try it out

Let's see how our install step went. To be safe, use the Clear site data button in the Application panel of DevTools to clear everything away and make sure we're starting fresh. If you previously installed the app, be sure to uninstall it, otherwise the install icon won't show up again.

Verify the install button is visible

First, let's verify our install icon shows up properly. Be sure to try this on both desktop and mobile.

  1. Open the URL in a new Chrome tab.
  2. Open Chrome's three-dot menu (next to the address bar).
    ▢ Verify you see " Install Weather... " in the menu.
  3. Refresh the weather data using the refresh button in the upper right corner to ensure we meet the user engagement heuristics .
    ▢ Verify that the install icon is visible in the app header.

Verify the install button works

Next, let's make sure everything installs properly and our events are properly fired. You can do this either on desktop or mobile. If you want to test this on mobile, be sure you're using remote debugging so you can see what's logged to the console.

  1. Open Chrome, and in a new browser tab, navigate to your Weather PWA.
  2. Open DevTools and switch to the Console panel.
  3. Click the install button in the upper right corner.
    ▢ Verify the install button disappears
    ▢ Verify the install modal dialog is shown.
  4. Click Cancel.
    ▢ Verify " User dismissed the A2HS prompt " is shown in the console output.
    ▢ Verify the install button reappears.
  5. Click the install button again, then click the install button in the modal dialog.
    ▢ Verify " User accepted the A2HS prompt " is shown in the console output.
    ▢ Verify " Weather App was installed " is shown in the console output.
    ▢ Verify the Weather app is added to the place where you'd typically find apps.
  6. Launch the Weather PWA.
    ▢ Verify the app opens as a standalone app, either in an app window on desktop, or full screen on mobile.

.

Verify iOS installation works properly

Let's also check the behavior on iOS. If you have an iOS device, you can use that, or if you're on a Mac, try the iOS Simulator available with Xcode.

  1. Open Safari and in a new browser tab, navigate to your Weather PWA.
  2. Click the Share button.
  3. Scroll right and click on the Add to Home Screen button.
    ▢ Verify the title, URL, and icon are correct.
  4. Click Add.
    ▢ Verify the app icon is added to the home screen.
  5. Launch the Weather PWA from the home screen.
    ▢ Verify the app launches full screen.

Bonus: Detecting if your app is launched from the home screen

The display-mode media query makes it possible to apply styles depending on how the app was launched, or determine how it was launched with JavaScript.

@media all and (display-mode: standalone) {
  body {
    background-color: yellow;
  }
}

You can also check the display-mode media query in JavaScript to see if you're running in standalone .

Bonus: Uninstalling your PWA

Remember, the beforeinstallevent doesn't fire if the app is already installed, so during development you'll probably want to install and uninstall your app several times to make sure everything is working as expected.

Android

On Android, PWAs are uninstalled in the same way other installed apps are uninstalled.

  1. Open the app drawer.
  2. Scroll down to find the Weather icon.
  3. Drag the app icon to the top of the screen.
  4. Choose Uninstall.

ChromeOS

On ChromeOS, PWAs are easily uninstalled from the launcher search box.

  1. Open the launcher.
  2. Type " Weather " into the search box, your Weather PWA should appear in the results.
  3. Right click (alt-click) on the Weather PWA.
  4. Click Remove from Chrome...

macOS and Windows

On Mac and Windows, PWAs may be uninstalled through Chrome:

  1. In a new browser tab, open chrome://apps .
  2. Right click (alt-click) on the Weather PWA.
  3. Click Remove from Chrome...

You can also open the installed PWA, click the the three-dot context menu in the upper right corner, and choose " Uninstall Weather PWA... "

Congratulations, you've successfully built your first Progressive Web App!

You added a web app manifest to enable it to be installed and you added a service worker to ensure that your PWA is always fast and reliable. You learned how to use DevTools to audit an app and how it can help you improve your user experience.

You now know the key steps required to turn any web app into a Progressive Web App.

What's next?

Check out some of these codelabs...

Further reading

Reference docs