Библиотеки прогрессивных веб-приложений в рабочей среде

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

Мы начали с инструментов Service Worker, таких как sw-precache и sw-toolbox, которые сейчас используются тысячами брендов для автономного кэширования и мгновенной загрузки (при повторном посещении) в своих производственных мобильных устройствах. места:

В 2017 году, если вы не пользуетесь услугами Service Workers, вы оставляете выигрыш в производительности для вернувшихся пользователей.

Давайте сравним временные полосы до / после для PWA CNet’s Tech Today и Housing.com. Мы можем видеть, что первый просмотр занимает несколько секунд по сравнению со средним показателем 3G. Посмотрите на улучшение на 3–4 секунды Service Worker, кэширующее оболочку своего приложения, и данные о времени загрузки:

Ууу. Они почти мгновенные :) Такой подход помог сайтам загружаться и быстрее взаимодействовать с Service Worker. Это воспроизводит желаемую характеристику производительности нативных приложений - после установки (веб-приложения) первоначальные затраты на перезагрузку амортизируются и не имеют переменной задержки.

Сервис-воркеры о надежной работе. Не только офлайн-поддержка - Алекс Рассел, Chrome

Крупные сайты, такие как Twitter.com, которые недавно отправили 100% своего мобильного веб-трафика в свои PWA с помощью Service Worker, архитектуры оболочки приложения и шаблона PRPL, также добились аналогичных результатов:

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

Например, Flipkart кэширует свои статические ресурсы , поэтому при повторных посещениях первая значимая отрисовка происходит на 1,5 секунды быстрее, чем первая загрузка:

Как описано в разделе Производительность при запуске JavaScript, Service Worker также разрешает вам использовать кеширование кода V8 при первом выполнении вашего JavaScript, поэтому вы также получите более быстрое время запуска для JS.

Сервис-воркеры могут помочь не только в кэшировании.

Мы также отправили библиотеку для Offline Google Analytics, основанную на Service Worker и IndexedDB. Когда пользователь не в сети или у него нестабильное сетевое соединение, мы ставим в очередь его аналитические данные и публикуем их, как только они вернутся в Интернет. Это используется такими сайтами, как eBay Ads в Мексике, чтобы свести к минимуму потерю полезной статистики, когда пользователи находятся в пути:

После успешной реализации этой идеи на сайте Google I / O 2015 мы сочли ее достаточно полезной и захотели обобщить ее, чтобы любой мог ее использовать.

Хорошим дополнением к библиотеке офлайн-аналитики является Autotrack - помощник, упрощающий отслеживание событий аналитики, которые волнуют большинство людей. Он имеет плагины для изменения URL-адресов PWA / SPA, видимости элементов, пользовательской прокрутки, медиа-запросов, видимости страниц и многого другого. Эти плагины помогают производственным сайтам, таким как 1Password, легко отслеживать важные события без шаблонных накладных расходов:

Затем мы начали работу над библиотекой Web Push Notifications, но появилась возможность сотрудничать с Firebase над гораздо более приятным решением, поэтому мы также помогли выпустить Firebase Cloud Messaging. Это кроссплатформенное решение для обмена сообщениями, которое может отправлять уведомления на основе сообщений или данных и отлично работает с PWA.

Alibaba - это всего лишь одна из производственных PWA, использующих FCM сегодня:

Мы также внесли свой вклад в библиотеку web-push от Mozilla, альтернативу, которую ребята могут посмотреть в этом разделе.

Поскольку Service Worker является основной частью многих наших библиотек, нам
также потребовались некоторые утилиты для их модульного тестирования. Мы создали селен-помощник для сквозного тестирования в нескольких браузерах с использованием Selenium. Мы также написали sw-testing-helpers для управления Service Workers в тестах.

Начало работы с нашими библиотеками JavaScript

Лаборатории разработчиков Google для sw-preache, sw-toolbox и offline-analytics находятся в свободном доступе.

Генерация сервис-воркеров

Sw-precache (который также отлично работает с Webpack) создает для вас Service Worker. В самом простом случае вы можете предоставить ему каталог dist, и он предоставит разумные настройки по умолчанию для кэширования любых статических ресурсов в автономном режиме, чтобы они мгновенно загружались из Cache Storage API при повторных посещениях:

$ sw-precache --root=dist

Вы можете проверить правильность кэширования файлов с помощью Панели приложений Chrome DevTools. После загрузки страницы найдите Cache Storage, и вы должны увидеть записи, соответствующие предоставленному каталогу:

Также поддерживается передача сложных конфигураций с использованием --config <file>. Любые параметры из файла можно переопределить с помощью флага командной строки. Мы рекомендуем использовать внешний файл JavaScript для определения конфигураций с помощью module.exports. Например, предположим, что существует файл path / to / sw-precache-config.js, который содержит:

module.exports = {
  staticFileGlobs: [
    'app/css/**.css',
    'app/**.html',
    'app/images/**.*',
    'app/js/**.js'
  ],
  stripPrefix: 'app/',
  runtimeCaching: [{
    urlPattern: /this\\.is\\.a\\.regex/,
    handler: 'networkFirst'
  }]
};

Мы можем передать файл в интерфейс командной строки, также установив опцию подробного вывода:

sw-precache --config=path/to/sw-precache-config.js --verbose

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

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

const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
module.exports = {
  // ...
  plugins: [
    // ...
    new SWPrecacheWebpackPlugin({
      cacheId: 'my-cache',
      filename: 'service-worker.js',
      staticFileGlobs: [
        './public/images/**/*.{png,jpg,gif}',
        './public/scripts/**/*.js',
        './public/styles/**/*.css',
        './public/partials/**/*.html'
      ],
      stripPrefix: './public/'
    })
  ]
};

Интеграция sw-preache в систему сборки gulp

Чтобы использовать sw-precache в gulp, мы сначала импортируем плагин в начало нашего gulpfile:

const swPrecache = require('sw-precache');

Затем мы создаем задачу gulp и вызываем write на swPrecache следующим образом:

swPrecache.write(filePath, options, callback)

filePath - это расположение файла, в который будет записывать сервис-воркер. options - это объект, который определяет поведение сгенерированного сервис-воркера (полный список опций см. В документации на Github). Обратный вызов выполняется всегда. Это нужно для того, чтобы gulp узнал, когда асинхронная операция завершена. Если есть ошибка, она передается в обратный вызов. Если ошибок не обнаружено, обратному вызову передается null.

Давайте посмотрим на пример:

gulp.task('generate-service-worker', function(callback) {
  swPrecache.write('app/service-worker.js'), {
    //1
    staticFileGlobs: [
       'app/index.html',
       'app/js/bundle.js',
       'app/css/bundle.css',
       'app/img/**/*.{svg,png,jpg,gif}'
     ],
    // 2
    importScripts: [
       'app/node_modules/sw-toolbox/sw-toolbox.js',
       'app/js/toolbox-script.js'
     ],
    // 3
    stripPrefix: 'app/'
  }, callback);
});

Мы вызываем задачу gulp 'generate-service-worker' и передаем обратный вызов функции, чтобы сделать ее асинхронной.

swPrecache.write создает сервис-воркера со следующими параметрами:

  • Ресурсы в staticFileGlobs предварительно кэшированы, что означает, что созданный сервисный работник будет содержать install обработчик событий, который кэширует ресурсы.
  • Сценарии в importScripts включаются в сгенерированный сервис-воркер внутри importScripts метода. В этот пример мы включаем модуль sw-toolbox и скрипт, содержащий наши маршруты.
  • Префикс app/ удаляется из всех путей к файлам в staticFileGlobs, так что пути в сгенерированном сервис-воркере являются относительными.

Кэширование во время выполнения

Sw-toolbox - это бесплатная библиотека, которая позволяет вам перехватывать сетевые запросы в Service Worker и выполнять стратегию кэширования с ответом. Он работает без маршрутов, которые ведут себя как прослушиватели событий fetch ().

Маршрут перехватывает сетевые запросы, соответствующие шаблону URL и методу HTTP-запроса. Затем он отвечает на основе правил в обработчике запросов. sw-toolbox имеет около 5 встроенных обработчиков для покрытия наиболее распространенных стратегий кеширования:

Если вы знакомы с Express, sw-toolbox поддерживает шаблоны URL, используя синтаксис, аналогичный синтаксису маршрутизации.

toolbox.router.get('img/**/*.{png,jpg}', global.toolbox.cacheFirst);

Это перехватит запросы GET для любого файла PNG / JPG в папке img. Он обрабатывает запросы в соответствии со стратегией cacheFirst, сначала проверяя кеш на предмет ответа. Если это не удается, запрос отправляется в сеть. Если это удается, ответ добавляется в кеш.

Здесь также можно использовать полные домены, например, это кеширует ваши шрифты Google:

toolbox.router.get('https://fonts.googleapis.com/', toolbox.cacheFirst);

Мы также можем перехватывать GET-запросы к другому домену, используя маршрутизацию в стиле Express. Мы просто определяем свойство «origin» в наших параметрах (строка или RegExp), которое сопоставляется с полным источником URL-адреса.

toolbox.router.get('/(.*)', global.toolbox.cacheFirst, {
  origin: /\.googleapis\.com$/
});

Также можно использовать объект RegExp. Здесь мы определяем маршрут для запросов POST, которые начинаются с https://www.googleapis.com:

toolbox.router.post(/^https://www.googleapis.com\//, global.toolbox.networkFirst);

Совет. При проверке хранилища кэша вы можете различить, какой sw-toolbox кэширует, поскольку он управляет пространством имен $$$ toolbox-cache $$.

Более детальный контроль

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

  • Мы даем ему имя («продукты»)
  • Мы даем ему максимальный размер 12 элементов (используя параметр maxEntries)
  • Мы устанавливаем срок действия контента через день (24 часа = 86400 секунд)
toolbox.router.get('/(.*)', global.toolbox.cacheFirst, {
  cache: {
   name: 'products',
   maxEntries: 12,
   maxAgeSeconds: 86400
  },
  origin: /\.products\.com$/
});

Вы можете найти учебные пособия по sw-precache и sw-toolbox в нашем учебном материале под руководством инструктора по прогрессивным веб-приложениям.

Офлайн-сервис Google Analytics

Как упоминалось ранее, автономная Google Analytics может ретранслировать запросы аналитики, выполняемые пользователем в автономном режиме, когда сетевое соединение снова становится доступным. Чтобы добавить это к вашему Service Worker, нужно всего две строчки кода:

// Import offline analytics into the SW global scope:
importScripts('path/to/offline-google-analytics-import.js');
// initialize it
goog.offlineGoogleAnalytics.initialize();

Бум. Вот и все!

Также можно предоставить объект с настраиваемыми параметрами, которые будут включены в каждый запрос, который воспроизводится:

goog.offlineGoogleAnalytics.initialize({
  parameterOverrides: {
    cd1: 'Guacamole',
    cd2: 'So much cheese'
  }
});

Примечание: основной вариант использования для передачи объекта переопределения параметров - это определение того, когда обращения отправляются нормально (а не воспроизводятся Service Worker).

Autotrack.js

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

<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', '<!-- your google_analytics_tracking_id -->', 'auto');
// Autotrack plugins available
ga('require', 'urlChangeTracker');
ga('require', 'cleanUrlTracker');    
ga('require', 'eventTracker');
ga('require', 'maxScrollTracker');
ga('require', 'outboundLinkTracker');  
ga('require', 'pageVisibilityTracker');    

ga('send', 'pageview');
</script>
<script async src='https://www.google-analytics.com/analytics.js'></script>
<script async src='/public/js/autotrack.js'></script>

Примечание. Некоторые плагины Autotrack.js работают без указания параметров конфигурации (например, outboundLinkTracker), а другие - нет (например, clearUrlTracker). Обязательно загляните в документацию, чтобы узнать, какие варианты поддерживают различные плагины :)

Селен Ассистент

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

Вы устанавливаете модули Web Driver для браузеров, которые хотите протестировать (npm install chromedriver и т. Д.), А затем можете перебирать список этих браузеров и управлять ими по мере необходимости. Помощник также отлично работает с Sauce Labs, если вам нужно протестировать браузеры, которые не установлены в вашей локальной системе.

Обмен сообщениями Firebase Cloud

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

1. Добавьте FCM gcm_sender_id в файл манифеста веб-приложения (manifest.json):

"gcm_sender_id": "103953800507"

2. Создайте новый файл firebase-messaging-service-worker.js. Мы собираемся предоставить
ему доступ к FCM, импортировав библиотеки Firebase Messaging в этот файл:

importScripts('https://www.gstatic.com/firebasejs/3.6.10/firebase-app.js')
importScripts('https://www.gstatic.com/firebasejs/3.6.10/firebase-messaging.js')

Затем инициализируйте приложение Firebase в Service Worker. Для этого передайте свой
messagingSenderId (из настроек проекта Firebase):

firebase.initializeApp({
  'messagingSenderId': '<-- your sender ID goes here -->'
});

Затем получите экземпляр Firebase Messaging для обработки фоновых
сообщений:

const messaging = firebase.messaging();

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

messaging.requestPermission()
.then(function() {
  console.log('Notification permissions granted.');
  // ...
})
.catch(function(err) {
  console.log('Permission denied', err);
});

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

Что дальше?

В настоящее время мы работаем над следующей большой версией наших библиотек Service Worker, расширяя наши исследования, чтобы также охватить Фоновую синхронизацию, переключение изображений HiDPI на основе Service Worker и более интеллектуальную аналитику для PWA. Мы с нетерпением ждем возможности поделиться новыми версиями по мере появления бета-версий этих библиотек.

Мы также планируем разместить новый пост на нашем канале Устойчивая загрузка о сервисных работниках в производстве на Google.com.

А пока мы надеемся, что наши библиотеки окажутся полезными независимо от того, создаете ли вы PWA или просто пытаетесь повысить производительность своего сайта :)

Выражаем благодарность замечательным членам нашей команды - Джеффу Поснику, Мэтту Гонту, Тейлору Сэвиджу, Джо Медли, Пратеку Бхатнагару, Лукасу Малленсу, Филу Уолтону, Алексу Расселу и бывшему участнику Мэт Скейлз за их вклад в нашу небольшую семью открытых -исходные библиотеки.

Ресурсы