Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

пример Embedded fallback - Жизненный цикл service worker для использования

Лекция



Это окончание невероятной информации про service worker.

...

border-box;">

// Регистрируем функцию на событие 'push'
self.addEventListener('push', function(event) {
  var payload = event.data ? event.data.text() : 'Alohomora';
  
  event.waitUntil(
    // Показываем уведомление с заголовком и телом сообщения.
    self.registration.showNotification('My first spell', {
      body: payload,
    })
  );
});

Rich Notifications


Усложним предыдущий вариант и добавим спецэффектов, здесь все зависит от ваших желаний и фантазии. Нам поможет полное Notification API. API предоставляет интерфейс для использования «живых» push-уведомлений пользователю c указанием локали, шаблоном вибрации, изображения.

Способ применения

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

service-worker.js
// Ключевое отличие по сравнению с Push Payload именно в использовании
// Notitfication API в SW

self.addEventListener('push', function(event) {
  var payload = event.data 
    // У нас все-таки волшебный мир с фантастическими тварями, поэтому
    // try.. catch мы не ставим ¯\_(ツ)_/¯
    ? JSON.parse(event.data)
    : {
      name: 'Expecto patronum!',
      icon: 'buck.jpg',
      locale: 'en'
    };
  
  event.waitUntil(
    // Показываем уведомление с заголовком и телом сообщения.
    // Устанавливаем иные параметры:
    // * язык
    // * шаблон вибрации
    // * изображение
    // имеется очень много параметров, о которых вы можете узнать тут
    // https://notifications.spec.whatwg.org/
    self.registration.showNotification('Summoning spell', {
      lang: payload.locale,
      body: payload.name,
      icon: payload.icon,
      vibrate: [500, 100, 500],
    })
  );
});

Push Tag


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

Способ применения

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

service-worker.js
var num = 1;

self.addEventListener('push', function(event) {
  event.waitUntil(
    // Показываем уведомление с заголовком и телом сообщения.
    // Число, которое увеличивается для каждого полученного уведомления.
    // Поле тега позволяет заменить старое уведомление на новое 
    // (уведомление с тем же тегом другого заменит его)
    self.registration.showNotification('Attacking Spell', {
      body: ++num > 1 ? 'Bombarda Maxima' : 'Bombarda',
      tag: 'spell',
    })
  );
});

Push Clients



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

Способ применения

Ниже код для трех случаев использования доставки уведомлений в зависимости от состояния приложения.

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

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

Самые классические примеры использования:

  • пришло сообщение в чат (Tinder),
  • интересная новость (Tproger),
  • обновилась задача в баг-трекере,
  • успешно/неуспешно прошел CI перед релизом,
  • клиент оплатил заказ/заключил сделку (будь то интернет-магазин или CRM).


Во всех этих случаях при клике в push клиенту откроется наше приложение или он будет сфокусирован уже на открытой вкладке.

service-worker.js
self.addEventListener('install', function(event) {
  event.waitUntil(self.skipWaiting());
});

self.addEventListener('activate', function(event) {
  event.waitUntil(self.clients.claim());
});

self.addEventListener('push', function(event) {
  event.waitUntil(
    // Получить список клиентов для SW
    self.clients.matchAll().then(function(clientList) {
          // Проверяем, есть ли хотя бы один сфокусированный клиент.
      var focused = clientList.some(function(client) {
        return client.focused;
      });

      var notificationMessage;
      if (focused) {
        notificationMessage = 'Imperio! You\'re still here, thanks!';
      } else if (clientList.length > 0) {
        notificationMessage = 'Imperio! You haven\'t closed the page, ' +
                              'click here to focus it!';
      } else {
        notificationMessage = 'Imperio! You have closed the page, ' +
                              'click here to re-open it!';
      }
      // Показывать уведомление с заголовком «Unforgiveable Curses»
      // и телом в зависимости от состоянию клиентов SW
      // (три разных тела: 
      // * 1, страница сфокусирована;
      // * 2, страница по-прежнему открыта, но не сфокусирована;
      // * 3, страница закрыта).
      return self.registration.showNotification('Unforgiveable Curses', {
        body: notificationMessage,
      });
    })
  );
});

// Регистрируем обработчик события 'notificationclick'.
self.addEventListener('notificationclick', function(event) {
  event.waitUntil(
    // Получаем список клиентов SW.
    self.clients.matchAll().then(function(clientList) {
      // Если есть хотя бы один клиент, фокусируем его.
      if (clientList.length > 0) {
        return clientList[0].focus();
      }
      // В противном случае открываем новую страницу.
      return self.clients.openWindow('our/url/page');
    })
  );
});

Push Subscription


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

Способ применения

После того, как SW зарегистрирован, клиент проверяет, подписан ли он на сервис уведомлений. В зависимости от этого устанавливается текст кнопки.

После успешной подписки (index.js::pushManager.subscribe) клиент отправляет post-запрос на сервер приложений для регистрации подписки.

Сервер периодически отправляет уведомление с помощью библиотеки web-push на все зарегистрированные эндпоинты. Если эндпоинт больше не зарегистрирован (подписка истекла или отменена), текущая подписка удаляется из списка подписок.

После успешной отписки (index.js::pushSubscription.unsubscribe) клиент отправляет post-запрос на сервер приложений, чтобы отменить регистрацию подписки. Сервер больше не отправляет уведомления. SW также следит за событиями pushsubscriptionchange и resubscribes.

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

index.js
// Для простоты будем использовать кнопку. 
// На боевой версии лучше использовать события.
var subscriptionButton = document.getElementById('subscriptionButton');

// Поскольку объект подписки требуется в нескольких местах, давайте создадим метод,
// который возвращает Promise.
function getSubscription() {
  return navigator.serviceWorker.ready
    .then(function(registration) {
      return registration.pushManager.getSubscription();
    });
}

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('service-worker.js')
    .then(function() {
      console.log('SW registered');
      subscriptionButton.removeAttribute('disabled');
    });
  getSubscription()
    .then(function(subscription) {
      if (subscription) {
        console.log('Already invaded', subscription.endpoint);
        setUnsubscribeButton();
      } else {
        setSubscribeButton();
      }
    });
}

// Получить «registration» от SW и создать новую
// подписку с помощью `registration.pushManager.subscribe`.
// Затем зарегистрировать новую подписку, отправив POST-запрос.
function subscribe() {
  navigator.serviceWorker.ready.then(function(registration) {
    return registration.pushManager.subscribe({ userVisibleOnly: true });
  }).then(function(subscription) {
    console.log('Legilimens!', subscription.endpoint);
    return fetch('register', {
      method: 'post',
      headers: {
        'Content-type': 'application/json'
      },
      body: JSON.stringify({
        endpoint: subscription.endpoint
      })
    });
  }).then(setUnsubscribeButton);
}

// Получить существующую подписку от SW,
// отменить подписку (`subscription.unsubscribe ()`) и 
// отменить регистрацию на сервере с помощью POST-запроса 
// для прекращения отправки push-сообщений.
function unsubscribe() {
  getSubscription().then(function(subscription) {
    return subscription.unsubscribe()
      .then(function() {
        console.log('Unsubscribed', subscription.endpoint);
        return fetch('unregister', {
          method: 'post',
          headers: {
            'Content-type': 'application/json'
          },
          body: JSON.stringify({
            endpoint: subscription.endpoint
          })
        });
      });
  }).then(setSubscribeButton);
}

// Для демонстрации (или тренировок). Изменяем текст кнопки.
function setSubscribeButton() {
  subscriptionButton.onclick = subscribe;
  subscriptionButton.textContent = 'Open mind!';
}

function setUnsubscribeButton() {
  subscriptionButton.onclick = unsubscribe;
  subscriptionButton.textContent = 'Protego!';
}

service-worker.js
// Слушаем событие 'push'.
self.addEventListener('push', function(event) {
  event.waitUntil(self.registration.showNotification('Your mind', {
    body: 'Wizard invaded to your mind!'
  }));
});

// Слушаем событие 'pushsubscriptionchange', которое запускается,
// когда истекает срок подписки. 
// Подписываемся снова и регистрируем новую подписку на сервере,
// отправив POST-запрос.
// На боевом приложении скорее всего будет использоваться ID или token
// для идентификации пользователя.
self.addEventListener('pushsubscriptionchange', function(event) {
  console.log('Spell expired');
  event.waitUntil(
    self.registration.pushManager.subscribe({ userVisibleOnly: true })
    .then(function(subscription) {
      console.log('Another invade! Legilimens!', subscription.endpoint);
      return fetch('register', {
        method: 'post',
        headers: {
          'Content-type': 'application/json'
        },
        body: JSON.stringify({
          endpoint: subscription.endpoint
        })
      });
    })
  );
});

server.js
var webPush = require('web-push');
var subscriptions = [];
var pushInterval = 10;

webPush.setGCMAPIKey(process.env.GCM_API_KEY || null);

// Отправляем уведомление push-сервису. 
// Удаляем подписку из общего массива `subscriptions`,
// если push-сервис отвечает на ошибку или подписка отменена или истекла.
function sendNotification(endpoint) {
  webPush.sendNotification({
    endpoint: endpoint
  }).then(function() {
  }).catch(function() {
    subscriptions.splice(subscriptions.indexOf(endpoint), 1);
  });
}

// В реальных условиях приложение отправляет уведовление только в случае
// возникновения события.
// Чтобы имитировать его, сервер отправляет уведомление каждые `pushInterval` секунд
// каждому подписчику
setInterval(function() {
  subscriptions.forEach(sendNotification);
}, pushInterval * 1000);

function isSubscribed(endpoint) {
  return (subscriptions.indexOf(endpoint) >= 0);
}

module.exports = function(app, route) {
  app.post(route + 'register', function(req, res) {
    var endpoint = req.body.endpoint;
    if (!isSubscribed(endpoint)) {
      console.log('We invaded into mind ' + endpoint);
      subscriptions.push(endpoint);
    }
    res.type('js').send('{"success":true}');
  });

  // Unregister a subscription by removing it from the `subscriptions` array
  app.post(route + 'unregister', function(req, res) {
    var endpoint = req.body.endpoint;
    if (isSubscribed(endpoint)) {
      console.log('It was counterspell from ' + endpoint);
      subscriptions.splice(subscriptions.indexOf(endpoint), 1);
    }
    res.type('js').send('{"success":true}');
  });
};

пример Embedded fallback


Существует проблема, когда браузер по умолчанию выдает вам сообщение о том, что вы офлайн. Я называю это проблемой, так как:

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

Жизненный цикл service worker для использования кеша и получения пуш уведомлений Жизненный цикл service worker для использования кеша и получения пуш уведомлений Жизненный цикл service worker для использования кеша и получения пуш уведомлений


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

Жизненный цикл service worker для использования кеша и получения пуш уведомлений
Решение

Нужно отдать fallback-данные, если нет доступа к ресурсам (сеть и кэш).
Данные подготавливаются заранее и кладутся как статичные ресурсы, доступные SW.

const CACHE = 'offline-fallback-v1';

// При установке воркера мы должны закешировать часть данных (статику).
self.addEventListener('install', (event) => {
    event.waitUntil(
        caches
            .open(CACHE)
            .then((cache) => cache.addAll(['/img/background']))
            // `skipWaiting()` необходим, потому что мы хотим активировать SW
            // и контролировать его сразу, а не после перезагрузки.
            .then(() => self.skipWaiting())
    );
});

self.addEventListener('activate', (event) => {
    // `self.clients.claim()` позволяет SW начать перехватывать запросы с самого начала,
    // это работает вместе с `skipWaiting()`, позволяя использовать `fallback` с самых первых запросов.
    event.waitUntil(self.clients.claim());
});

self.addEventListener('fetch', function(event) {
    // Можете использовать любую стратегию описанную выше.
    // Если она не отработает корректно, то используейте `Embedded fallback`.
    event.respondWith(networkOrCache(event.request)
        .catch(() => useFallback()));
});

function networkOrCache(request) {
    return fetch(request)
        .then((response) => response.ok ? response : fromCache(request))
        .catch(() => fromCache(request));
}

// Наш Fallback вместе с нашим собсвенным Динозавриком.
const FALLBACK =
    '
\n' + '
App Title
\n'
+ '
you are offline
\n'
+ ' dinosaur\n' + '
'
; // Он никогда не упадет, т.к мы всегда отдаем заранее подготовленные данные. function useFallback() { return Promise.resolve(new Response(FALLBACK, { headers: { 'Content-Type': 'text/html; charset=utf-8' }})); } function fromCache(request) { return caches.open(CACHE).then((cache) => cache.match(request).then((matching) => matching || Promise.reject('no-match') )); }

Еще разок о кодировании


Выше мы рассмотрели магические способы использования SW и Web Push для приложений.
Данный тандем таит в себе множество интересных применений.

Если вам нужно лишь иногда зазывать пользователя к себе в приложение или сообщать ему о об исправлениях или изменении статуса его заказа, то используйте Push Payload. Можно добавить немного фантазии и заиспользовать Notification API — тогда цвета и иконка вашего приложения будет видны пользователю в Rich Push.

Если же вы желаете завладеть всем вниманием пользователи установить с ним контакт — примеры Push Client и Push Subscription для вас

Жду ваших комментариев и пожеланий на следующую тему. От себя добавлю, что хотелось бы поговорить и обсудить тему работы SW + React/Redux-приложений и способы ускорения. Будет полезно?

Продолжение:


Часть 1 Жизненный цикл service worker для использования кеша и получения пуш уведомлений
Часть 2 пример Embedded fallback - Жизненный цикл service worker для использования

создано: 2023-12-10
обновлено: 2024-11-14
69



Рейтиг 9 of 10. count vote: 2
Вы довольны ?:


Поделиться:

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей

Комментарии


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

Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)

Термины: Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)