PWA

Согласно этой статье из CNBC, к 2025 году почти три четверти населения мира будут использовать для доступа к интернету только смартфоны. Поэтому, если вы планируете создать новый веб-сайт или редизайнить старый, вам следует задуматься о создании PWA.

PWA — идеальное начало для любого проекта, не требующее большого количества усилий, а созданное приложение работает на всех платформах!

Что такое PWA?

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

Зачем создавать PWA?

На сайте pwastats.com представлена статистика самых известных компаний, использующих PWA, которые улучшили производительность своих веб-сайтов более чем на 100%.

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

Кто уже использует PWA?

Uber, Instagram, Twitter, Pinterest, Forbes, Alibaba

Как видим, некоторые из крупнейших в мире компаний, такие как Twitter, Instagram, Uber, Pinterest, Forbes, Alibaba и другие, уже используют PWA.

Как создать PWA

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

Манифест веб-приложения

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

Свойства:

  • name: имя, используемое в запросе на установку приложения.
  • short_name: короткое имя, используемое на домашнем экране пользователя, в программе запуска или в других местах с ограничением места.
  • start_url: сообщает браузеру стартовый URL-адрес приложения.
  • display: предоставляет возможность настройки отображения UI браузера при запуске приложения. Наиболее часто используемое значение — standalone: оно открывает веб-приложение, которое выглядит как автономное нативное приложение.
  • background_color: используется на заставке при запуске приложения.
  • theme_color: устанавливает цвет панели инструментов.
  • orientation: усиливает определенную ориентацию.
  • scope: определяет набор URL-адресов, находящихся в приложении и обычно используется для определения момента выхода пользователя из приложения.
  • icons: когда пользователь добавляет сайт на домашний экран, можно определить набор используемых браузером изображений.

Сервис-воркер

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

  • выполнять перехват сетевых запросов
  • кэшировать или извлекать ресурсы из кеша
  • доставлять push-увевомления

Жизненный цикл сервис-воркера

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

Создание PWA шаг за шагом за 5 минут

Пришло время реализовать прогрессивное веб-приложение!

Прежде чем начать, установим расширение Lighthouse — инструмент (от Google) для улучшения качества веб-страниц, который выдает следующий отчет:

Отчет Lighthouse

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

Установить расширение для Chrome можно здесь.

Структура файла в нашем примере выглядит так:

Структура файла

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

В index.html вызываем manifest.json:

<link rel="manifest" href="/manifest.json">

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

<link rel="apple-touch-icon" href="/assets/images/logo-96x96.png">
<meta name="apple-mobile-web-app-status-bar" content="#FFE1C4">
<meta name="theme-color" content="#FFE1C4">
<script src="/assets/js/app.js"></script>

Это основные теги, но, конечно, их будет еще больше, а также у них могут быть разные пути!

Статический кэш

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

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

Начнем с manifest.json

{
  "name": "Name Website",
  "short_name": "NameWebsite",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#F4F4F4",
  "theme_color": "#F4F4F4", 
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/assets/images/logo-72x72.png",
      "type": "image/png", 
      "sizes": "72x72"
    },
    {
      "src": "/assets/images/logo-96x96.png",
      "type": "image/png",
      "sizes": "96x96"
    },
    {
      "src": "/assets/images/logo-128x128.png",
      "type": "image/png",
      "sizes": "128x128"
    },
    {
      "src": "/assets/images/logo-144x144.png",
      "type": "image/png",
      "sizes": "144x144"
    },
    {
      "src": "/assets/images/logo-152x152.png",
      "type": "image/png",
      "sizes": "152x152"
    },
    {
      "src": "/assets/images/logo-192x192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "/assets/images/logo-384x384.png",
      "type": "image/png",
      "sizes": "384x384"
    }, 
    {
      "src": "/assets/images/logo-512x512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ]
}

Теперь нужно проверить, допускает ли браузер работу воркера, и, если да, то регистрируем воркеры в app.js.

if('serviceWorker' in navigator){
  navigator.serviceWorker.register('/sw.js')
    .then(reg => console.log('service worker registered'))
    .catch(err => console.log('service worker not registered', err));
}

Теперь запишем воркер в файл sw.js:

const staticCacheName = 'site-static-v1';
const assets = [
  '/',
  '/index.html',
  '/assets/js/ui.js',
  '/assets/css/main.css',
  '/assets/images/background-home.jpg',
  'https://fonts.googleapis.com/css?family=Lato:300,400,700',
];

// событие install
self.addEventListener('install', evt => {
  evt.waitUntil(
    caches.open(staticCacheName).then((cache) => {
      console.log('caching shell assets');
      cache.addAll(assets);
    })
  );
});

// событие activate
self.addEventListener('activate', evt => {
  evt.waitUntil(
    caches.keys().then(keys => {
      return Promise.all(keys
        .filter(key => key !== staticCacheName)
        .map(key => caches.delete(key))
      );
    })
  );
});

// событие fetch
self.addEventListener('fetch', evt => {
  evt.respondWith(
    caches.match(evt.request).then(cacheRes => {
      return cacheRes || fetch(evt.request);
    })
  );
});

В массиве хранятся все ресурсы, которые нужно поместить в кэш.

Событие Install

Добавляем в кэш все статические ассеты, которые отображаются в консоли Chrome:

Скрин кэша

Событие Activate

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

Событие Fetch

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

Скрин сети

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

Динамический кэш

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

В предыдущей конфигурации необходимо изменить файл sw.js следующим образом:

const dynamicCacheName = 'site-dynamic-v1';

// событие activate
self.addEventListener('activate', evt => {
  evt.waitUntil(
    caches.keys().then(keys => {
      return Promise.all(keys
        .filter(key =>  key !== dynamicCacheName)
        .map(key => caches.delete(key))
      );
    })
  );
});

// событие fetch
self.addEventListener('fetch', evt => {
  evt.respondWith(
    caches.match(evt.request).then(cacheRes => {
      return cacheRes || fetch(evt.request).then(fetchRes => {
        return caches.open(dynamicCacheName).then(cache => {
          cache.put(evt.request.url, fetchRes.clone());
          return fetchRes;
        })
      });
    })
  );
});

Событие Active

Здесь мы выполняем те же действия, что и для статического кэша.

Событие Fetch

В событии fetch мы автоматически помещаем в кэш все fetch-запросы.

Если вы попробуете оба вида кэша, а затем запустите Lighthouse, то увидите, что сайт является PWA.

PWA Lighthouse

Здесь можно найти код статических и динамических кэшей.

Статический или динамический: какой следует использовать?

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

Более подробно о PWA можно узнать здесь.

Читайте также:


Перевод статьи Luca SpezzanoTurn Your Website into a PWA