По мотивам статьи

Что это такое?

Прогрессивное web-приложение (англ. progressive web app, PWA) — технология в web-разработке, которая визуально и функционально трансформирует сайт в приложение (мобильное приложение в браузере).

PWA совмещает в себе свойства нативного приложения и функции браузера, что имеет свои преимущества:

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

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

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

Полная форма PWA — прогрессивные веб-приложения. PWA — это веб-страница HTML 5. Это веб-приложение, похожее на мобильное приложение, которое хранится непосредственно на мобильном телефоне с веб-сайта. Другими словами, это веб-сайт, который работает в мобильном браузере пользователя. PWA — это новая технология. PWA позволяет сохранить веб-сайт на вашем устройстве. Он создает значок в виде веб-приложения, и этот значок при открытии выглядит как мобильное приложение.

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

Как правило, когда вам нужно создавать приложения, вы должны разрабатывать это приложение для разных платформ, таких как iOS, Android и Windows. Но в случае с PWA вам не нужно разрабатывать отдельные приложения для разных платформ. Вам нужно только создать веб-страницы на основе HTML 5, которые могут работать в любом мобильном браузере.

PWA features Источник: Java T Pot.

Прогрессивные веб-приложения имеют следующие характеристики, которые важно понимать:

Преимущества PWA:

Недостатки PWA:

Не беспокойтесь об этих новых терминах — мы рассмотрим их ниже.

Во-первых, нам нужно создать наше традиционное веб-приложение. Итак, начнем с разметки.

Разметка

После поиска в Интернете простого руководства по PWA все, что я нашел, было либо слишком сложным, либо требовало одной сторонней библиотеки/фреймворка/платформы или другого. Лично я, изучая новую технологию, предпочел бы не отвлекаться на ненужные детали. Поэтому, опираясь на ряд источников, я сам написал простой учебник, который не требует стороннего контента: классическое приложение «Hello World», стиль PWA.

Создайте каталог для приложения и добавьте подкаталоги js, css и images. Это должно выглядеть так, когда вы закончите:

/Hello-PWA # Папка проекта /css # Таблицы стилей /js # JavaScript /images # Файлы изображений.

При написании разметки для Progressive Web App необходимо помнить о двух требованиях:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Hello World</title>
  <link rel="stylesheet" href="css/style.css">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
<body class="fullscreen">
  <div class="container">
    <h1 class="title">Hello World!</h1>
  </div>
</body>
</html>

HTML-файл относительно прост. Оборачиваем все в основной тег.

В index.html

И создайте панель навигации с тегом nav. Затем div с классом .container будет содержать наши карты, которые мы добавим позже с помощью JavaScript.

Теперь, когда мы разобрались с этим, давайте стилизуем его с помощью CSS.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link rel="stylesheet" href="css/style.css" />
    <title>Dev'Coffee PWA</title>
  </head>
  <body>
    <main>
      <nav>
        <h1>Dev'Coffee</h1>
        <ul>
          <li>Home</li>
          <li>About</li>
          <li>Blog</li>
        </ul>
      </nav>
      <div class="container"></div>
    </main>
    <script src="js/app.js"></script>
  </body>
</html>

Все, что делает эта разметка, это загружает таблицу стилей и устанавливает ширину и масштаб окна просмотра по умолчанию. Текст «привет» жестко закодирован в элементе h1 body, который заключен в контейнер div для упрощения стилизации.

Затем создайте файл с именем style.css в папке css и добавьте этот код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
body {
  font-family: sans-serif;
}

/* Make content area fill the entire browser window */
html,
.fullscreen {
  display: flex;
  height: 100%;
  margin: 0;
  padding: 0;
  width: 100%;
}

/* Center the content in the browser window */
.container {
  margin: auto;
  text-align: center;
}

.title {
  font-size: 3rem;
}

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

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

Теперь вы можете протестировать приложение, нажав кнопку предварительного просмотра в скобках. (Молния в верхнем правом углу.) Откроется окно Chrome и откроется ваша страница.

Теперь, когда у нас есть что-то в браузере, мы воспользуемся Google Lighthouse, чтобы протестировать приложение и посмотреть, насколько хорошо оно соответствует стандартам PWA. Нажмите F12, чтобы открыть панель разработчика в Chrome, и щелкните вкладку аудита, чтобы открыть Lighthouse.

Lighthouse Источник: Meduim.

Убедитесь, что опция «Прогрессивное веб-приложение» отмечена. Остальные пока можно снять. Затем нажмите на кнопку «Запустить тесты». Через минуту или две Lighthouse должен предоставить вам оценку и список проверок, которые приложение прошло или не прошло. На этом этапе приложение должно набрать около 45 баллов. Если все было закодировано правильно, вы заметите, что большинство тестов, которые он проходит, связаны с требованиями, которые мы изложили в начале:

  1. Содержит некоторый контент, когда JavaScript недоступен.
  2. Имеет тег <meta name="viewport"> с шириной или начальным масштабом.
  3. Контент имеет правильный размер для области просмотра.

Простой JavaScript

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

Как я уже говорил ранее, класс .container будет держать наши карты. Поэтому нам нужно его выбрать.

1
2
3
4
5
6
7
8
9
10
11
12
const container = document.querySelector(".container")
const coffees = [
  { name: "Perspiciatis", image: "images/coffee1.jpg" },
  { name: "Voluptatem", image: "images/coffee2.jpg" },
  { name: "Explicabo", image: "images/coffee3.jpg" },
  { name: "Rchitecto", image: "images/coffee4.jpg" },
  { name: " Beatae", image: "images/coffee5.jpg" },
  { name: " Vitae", image: "images/coffee6.jpg" },
  { name: "Inventore", image: "images/coffee7.jpg" },
  { name: "Veritatis", image: "images/coffee8.jpg" },
  { name: "Accusantium", image: "images/coffee9.jpg" },
]

Затем мы создаем массив карточек с именами и изображениями.

С помощью приведенного выше кода мы теперь можем просмотреть массив и отобразить их в HTML-файле. И чтобы все заработало, мы ждем, пока содержимое DOM (Document Object Model) завершит загрузку, чтобы запустить метод showCoffees.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const showCoffees = () => {
  let output = ""
  coffees.forEach(
    ({ name, image }) =>
      (output += `
              <div class="card">
                <img class="card--avatar" src=${image} />
                <h1 class="card--title">${name}</h1>
                <a class="card--link" href="#">Taste</a>
              </div>
              `)
  )
  container.innerHTML = output
}

document.addEventListener("DOMContentLoaded", showCoffees)

Превращение сайта в PWA

Манифест

Этот код просто загружает скрипт сервисного работника и запускает его.

Добавьте код в свое приложение, включив скрипт непосредственно перед закрывающим тегом </body> в index.html.

1
2
3
4
5
6
7
8
9
{
  "name": "Hello World",
  "short_name": "Hello",
  "lang": "en-US",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "white",
  "theme_color": "white"
}

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

Построчно поля выглядят следующим образом:

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

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

lang Язык по умолчанию, на котором локализовано приложение. В нашем случае английский.

start_url Сообщает браузеру, какую страницу загружать при запуске приложения. Обычно это index.html, но это не обязательно.

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

background_color Цвет заставки, которая открывается при запуске приложения.

theme_color Задает цвет панели инструментов и переключателя задач.

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

1
2
3
4
5
6
<head>
...
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="white"/>
...
</head>

Вы также должны объявить цвет темы таким, чтобы он соответствовал цвету, установленному в вашем манифесте, добавив метатег внутри заголовка.

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

Чтобы правильно добавить эту функцию, вам понадобится значок приложения, размер которого подходит для браузера, Windows, Mac/iPhone и Android. Это как минимум 7 различных размеров: 128 x 128 пикселей, 144 x 144 пикселей, 152 x 152 пикселей, 192 x 192 пикселей, 256 x 256 пикселей, 512 x 512 пикселей и значок 16 x 16 пикселей. Вместо того, чтобы создавать свои собственные, вы можете загрузить те, которые я создал для этого руководства, с Github. Сохраните их в папке с изображениями и поместите favicon.ico в корневую папку проекта.

Добавьте значки в файл манифеста после свойства short_name следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
  "name": "Hello World",
  "short_name": "Hello",
  "icons": [{
    "src": "images/hello-icon-128.png",
      "sizes": "128x128",
      "type": "image/png"
    }, {
      "src": "images/hello-icon-144.png",
      "sizes": "144x144",
      "type": "image/png"
    }, {
      "src": "images/hello-icon-152.png",
      "sizes": "152x152",
      "type": "image/png"
    }, {
      "src": "images/hello-icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    }, {
      "src": "images/hello-icon-256.png",
      "sizes": "256x256",
      "type": "image/png"
    }, {
      "src": "images/hello-icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }],
  "lang": "en-US",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "white",
  "theme_color": "white"
}

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

Теперь, когда мы знаем, что такое веб-манифест, давайте создадим новый файл с именем manifest.json (вы должны назвать его так) в корневом каталоге. Затем добавьте этот блок кода ниже.

В манифесте.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
  "name": "Dev'Coffee",
  "short_name": "DevCoffee",
  "start_url": "index.html",
  "display": "standalone",
  "background_color": "#fdfdfd",
  "theme_color": "#db4938",
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/images/icons/icon-72x72.png",
      "type": "image/png", "sizes": "72x72"
    },
    // ...
    {
      "src": "/images/icons/icon-512x512.png",
      "type": "image/png", "sizes": "512x512"
    }
  ]
}

В конце концов, это просто файл JSON с некоторыми обязательными и необязательными свойствами.

имя: Когда браузер запускает заставку, это будет имя, отображаемое на экране.

short_name: это будет имя, отображаемое под ярлыком вашего приложения на главном экране.

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

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

background_color: Когда браузер запускает заставку, это будет фон экрана.

theme_color: это будет цвет фона строки состояния, когда мы открываем приложение.

ориентация: указывает браузеру ориентацию при отображении приложения.

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

Манифест веб-приложения — это первый компонент PWA. Это простой файл JSON, который управляет приложением пользователя. Обычно он называется «manifest.json». Это самый важный компонент для присутствия PWA. Когда вы впервые подключаете PWA к сети, мобильный браузер считывает файл «manifest.json» и сохраняет его локально в кэш-памяти.

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

Файл «manifest.json» помогает PWA выглядеть как нативное приложение. С помощью файла manifest.json разработчик PWA может управлять тем, как приложение отображается на экране мобильного устройства пользователя. Разработчик PWA также может установить тему для экрана-заставки мобильного телефона и адресной строки приложения.

Файл «manifest.json» позволяет разработчику PWA искать централизованное место для метаданных веб-приложения. Файл JSON определяет ссылки на значки и размеры значков, полное и сокращенное имя приложения, типы, цвет фона, тему, местоположения и другие визуальные данные, необходимые для взаимодействия с приложением.

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

1
2
3
4
5
6
7
8
9
10
11
12
<link rel="manifest" href="manifest.json" />
<!-- ios support -->
<link rel="apple-touch-icon" href="images/icons/icon-72x72.png" />
<link rel="apple-touch-icon" href="images/icons/icon-96x96.png" />
<link rel="apple-touch-icon" href="images/icons/icon-128x128.png" />
<link rel="apple-touch-icon" href="images/icons/icon-144x144.png" />
<link rel="apple-touch-icon" href="images/icons/icon-152x152.png" />
<link rel="apple-touch-icon" href="images/icons/icon-192x192.png" />
<link rel="apple-touch-icon" href="images/icons/icon-384x384.png" />
<link rel="apple-touch-icon" href="images/icons/icon-512x512.png" />
<meta name="apple-mobile-web-app-status-bar" content="#db4938" />
<meta name="theme-color" content="#db4938" />

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

Теперь мы можем погрузиться в заключительную часть и представить сервис-воркера.

Service Worker

Service Worker Источник: DZone.

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var cacheName = 'hello-pwa';
var filesToCache = [
  '/',
  '/index.html',
  '/css/style.css',
  '/js/main.js'
];

/* Start the service worker and cache all of the app's content */
self.addEventListener('install', function(e) {
  e.waitUntil(
    caches.open(cacheName).then(function(cache) {
      return cache.addAll(filesToCache);
    })
  );
});

/* Serve cached content when offline */
self.addEventListener('fetch', function(e) {
  e.respondWith(
    caches.match(e.request).then(function(response) {
      return response || fetch(e.request);
    })
  );
});

Первые строки скрипта объявляют две переменные: cacheName и filesToCache. cacheName используется для создания автономного кеша в браузере и предоставления нам доступа к нему из Javascript. filesToCache — это массив, содержащий список всех файлов, которые необходимо кэшировать. Эти файлы должны быть записаны в виде URL-адресов. Обратите внимание, что первый — это просто «/», базовый URL-адрес. Это делается для того, чтобы браузер кэшировал index.html, даже если пользователь не вводит это имя файла напрямую.

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

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

Теперь, когда скрипт Service Worker создан, нам нужно зарегистрировать его в нашем приложении. Создайте файл с именем main.js в папке js и введите следующий код:

1
2
3
4
5
6
7
8
window.onload = () => {
  'use strict';

  if ('serviceWorker' in navigator) {
    navigator.serviceWorker
             .register('./sw.js');
  }
}

Этот код просто загружает скрипт сервисного работника и запускает его.

Добавьте код в свое приложение, включив скрипт непосредственно перед закрывающим тегом </body> в index.html.

1
2
3
4
...
</div>
  <script src="js/main.js"></script>
</body>

Теперь мы можем погрузиться в заключительную часть и представить сервис-воркера.

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

Кэшировать активы В сервисворкер.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const staticDevCoffee = "dev-coffee-site-v1"
const assets = [
  "/",
  "/index.html",
  "/css/style.css",
  "/js/app.js",
  "/images/coffee1.jpg",
  "/images/coffee2.jpg",
  "/images/coffee3.jpg",
  "/images/coffee4.jpg",
  "/images/coffee5.jpg",
  "/images/coffee6.jpg",
  "/images/coffee7.jpg",
  "/images/coffee8.jpg",
  "/images/coffee9.jpg",
]

self.addEventListener("install", installEvent => {
  installEvent.waitUntil(
    caches.open(staticDevCoffee).then(cache => {
      cache.addAll(assets)
    })
  )
})

Сначала этот код выглядит устрашающе, но это всего лишь JavaScript (так что не волнуйтесь).

Мы объявляем имя нашего кеша staticDevCoffee и ресурсы для хранения в кеше. И чтобы выполнить это действие, нам нужно прикрепить слушателя к себе.

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

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

Когда событие установки запускается, мы запускаем обратный вызов, который дает нам доступ к объекту события.

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

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

Когда API кеша готов, мы можем запустить метод open() и создать наш кеш, передав его имя в качестве аргумента в caches.open(staticDevCoffee).

Затем он возвращает обещание, которое помогает нам хранить наши активы в кеше с помощью cache.addAll(assets).

Надеюсь, ты все еще со мной.

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

Итак, давайте достанем наш кеш.

Использование кеша

1
2
3
4
5
6
7
self.addEventListener("fetch", fetchEvent => {
  fetchEvent.respondWith(
    caches.match(fetchEvent.request).then(res => {
      return res || fetch(fetchEvent.request)
    })
  )
})

Здесь мы используем событие fetch, чтобы вернуть наши данные. Обратный вызов дает нам доступ к fetchEvent. Затем мы присоединяем responseWith(), чтобы предотвратить ответ браузера по умолчанию. Вместо этого он возвращает обещание, потому что для завершения действия выборки может потребоваться время.

И когда кеш готов, мы применяем caches.match(fetchEvent.request). Он проверит, соответствует ли что-то в кеше fetchEvent.request. Кстати, fetchEvent.request — это просто наш массив ассетов.

Затем он возвращает обещание. И, наконец, мы можем вернуть результат, если он существует, или начальную выборку, если нет.

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

И самое главное, это делает наше приложение доступным в автономном режиме.

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

Регистрация Service Worker

1
2
3
4
5
6
7
8
if ("serviceWorker" in navigator) {
  window.addEventListener("load", function() {
    navigator.serviceWorker
      .register("/serviceWorker.js")
      .then(res => console.log("service worker registered"))
      .catch(err => console.log("service worker not registered", err))
  })
}

Здесь мы начинаем с проверки, поддерживается ли serviceWorker текущим браузером (поскольку он все еще поддерживается не всеми браузерами).

Затем мы слушаем событие загрузки страницы, чтобы зарегистрировать нашего работника службы, передав имя нашего файла serviceWorker.js в navigator.serviceWorker.register() в качестве параметра для регистрации нашего работника.

С этим обновлением мы превратили наше обычное веб-приложение в PWA.

Демонстрация

Обзор сервисного работника

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

Что предоставляют сервисные работники?

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

Постепенно улучшающийся жизненный цикл, подобный приложению

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

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

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

Доступ к API кэширования на основе JavaScript

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

В то время как на кэш HTTP влияют директивы кэширования, указанные в заголовках HTTP, интерфейс Cache программируется с помощью Javascript. Это означает, что кеширование ответов на сетевые запросы может быть основано на любой логике, которая лучше всего подходит для данного веб-сайта. Например:

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

Асинхронный и управляемый событиями API

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

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

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

Предварительное кэширование и кэширование во время выполнения

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

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

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

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

Изоляция от основного потока

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

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

Создание PWA на React

Прогрессивные веб-приложения (PWA) быстро приобрели популярность как быстродействующие, ориентированные на производительность веб-приложения, оптимизированные для предоставления возможностей, подобных мобильным приложениям. PWA создаются с использованием передовых технологий, таких как HTML, CSS и JavaScript, чтобы обеспечить уровень удобства использования и производительности, сравнимый с родными мобильными приложениями. Они быстро реагируют, потребляют меньше данных и места для хранения, поддерживают push-уведомления и автономное использование в браузере.

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

Настройка приложения React

Сначала создайте приложение React с помощью create-react-app. Для этого вам необходимо выполнить следующие команды:

1
2
npm install -g create-react-app
create-react-app pwa-app

Теперь установите React Router:

1
2
3
cd pwa-app

npm install --save react-router@3.0.5

Вам нужно заменить содержимое «src/App.js», используя приведенный ниже код, чтобы получить базовый шаблон с навигацией.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import React, { Component } from 'react';
import { Router, browserHistory, Route, Link } from 'react-router';
import './App.css';

const NavBar = () => (
  <div className="navbar">
    <Link to="/">Feed</Link>
    <Link to="/profile">Profile</Link>
  </div>
);

const Template = ({ title }) => (
  <div>
    <NavBar />
    <p className="page-info">
      This is the {title} page.
    </p>
  </div>
);

const Feed = (props) => (
  <Template title="Feed"/>
);

const Profile = (props) => (
  <Template title="Profile"/>
);

class App extends Component {
  render() {
    return (
      <Router history={browserHistory}>
        <Route path="/" component={Feed}/>
        <Route path="/profile" component={Profile}/>
      </Router>
    );
  }
}
export default App;

Теперь вам нужно будет обновить стили по умолчанию, заменив ваш «src/App.css» стилями ниже, чтобы ваше приложение выглядело чистым.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.navbar {
  background-color: #01C8E5;
  text-align: center;
}

.navbar a {
  display: inline-block;
  padding: 10px;
  color: #fff;
  text-decoration: none;
}

.page-info {
  text-align: center;
  font-weight: bold;
}

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// Flag for enabling cache in production
var doCache = false;

var CACHE_NAME = 'pwa-app-cache';

// Delete old caches
self.addEventListener('activate', event => {
  const currentCachelist = [CACHE_NAME];
  event.waitUntil(
    caches.keys()
      .then(keyList =>
        Promise.all(keyList.map(key => {
          if (!currentCachelist.includes(key)) {
            return caches.delete(key);
          }
        }))
      )
  );
});

// This triggers when user starts the app
self.addEventListener('install', function(event) {
  if (doCache) {
    event.waitUntil(
      caches.open(CACHE_NAME)
        .then(function(cache) {
          fetch('asset-manifest.json')
            .then(response => {
              response.json();
            })
            .then(assets => {
              // We will cache initial page and the main.js
              // We could also cache assets like CSS and images
              const urlsToCache = [
                '/',
                assets['main.js']
              ];
              cache.addAll(urlsToCache);
            })
        })
    );
  }
});

// Here we intercept request and serve up the matching files
self.addEventListener('fetch', function(event) {
  if (doCache) {
    event.respondWith(
      caches.match(event.request).then(function(response) {
        return response || fetch(event.request);
      })
    );
  }
});

Теперь проверьте, поддерживают ли браузеры сервис-воркеры, и после этого зарегистрируйте worker.js. Для этого вам нужно добавить следующий скрипт в файл public/index.html (обратите внимание, что shrink-to-fit=no из метатега окна просмотра было удалено).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#000000">
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <title>React App</title>
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
    <script>
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
          navigator.serviceWorker.register('worker.js').then(function(registration) {
            console.log('Worker registration successful', registration.scope);
          }, function(err) {
            console.log('Worker registration failed', err);
          }).catch(function(err) {
            console.log(err);
          });
        });
      } else {
        console.log('Service Worker is not supported by browser.');
      }
    </script>
  </body>
</html>

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

Улучшение прогрессивного характера приложения

Ваше приложение будет отображать пустой корневой div до тех пор, пока не загрузится JavaScript и React не перехватит первоначальный маршрут. Вы должны убедиться, что ваше приложение работает без какой-либо загрузки JS и отображает немного CSS и HTML, прежде чем React вступит в игру. Ваш обновленный index.html будет выглядеть так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#000000">
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <title>React App</title>
    <style type="text/css">
    <!-- ... -->
    </style>
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root">
      <div class="navbar">
        <a href="/">Feed</a>
      </div>
      <p class="page-info">
        Loading an awesome app...
      </p>
    </div>
    <script>
    <!-- ... -->
    </script>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    <style type="text/css">
      body {
        margin: 0;
        padding: 0;
        font-family: sans-serif;
      }
      .navbar {
        background-color: #01C8E5;
        text-align: center;
      }
      .navbar a {
        display: inline-block;
        padding: 10px;
        color: #fff;
        text-decoration: none;
      }
      .page-info {
        text-align: center;
        font-weight: bold;
      }
    </style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    <script>
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', function() {
          navigator.serviceWorker.register('worker.js').then(function(registration) {
            console.log('Worker registration successful', registration.scope);
          }, function(err) {
            console.log('Worker registration failed', err);
          }).catch(function(err) {
            console.log(err);
          });
        });
      } else {
        console.log('Service Worker is not supported by browser.');
      }
    </script>

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

Теперь отсутствует только HTTPS, а кэширование можно исправить после развертывания приложения. Обновите флаг doCache на «true» в файле worker.js. Создайте новый проект в firebase-console и назовите его «Приложение Pwa». Затем выполните следующую команду в каталоге проекта:

1
2
3
npm install -g firebase-tools
firebase login
firebase init

Ваш firebase.json должен выглядеть так:

1
2
3
4
5
6
7
8
9
10
11
{
  "hosting": {
    "public": "build",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

После инициализации создайте и разверните приложение.

1
2
3
npm run build

firebase deploy

Progressive Web Apps

Progressive Web Apps