Service Workers. Инструкция по применению



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

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

    До появления SW проблему работы в offline-режиме решал другой API — AppCache. Однако наряду с подводными камнями AppCache фигурировал факт, что он отлично работает в single-page приложениях, но не очень хорошо подходит для многостраничных сайтов. SW разрабатывались, чтобы избежать этих проблем.

    Что такое Service Worker?


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

    Во-вторых, SW запускается в worker контексте, поэтому он не имеет доступа к DOM и работает в потоке, отдельном от основного потока JavaScript, управляющего вашим приложением, а следовательно — не блокирует его. Он призван быть полностью асинхронным, поэтому использовать синхронные API (XHR и LocalStorage) в SW нельзя.

    В-третьих, из соображений безопасности SW работают только по HTTPS, так как давать посторонним людям возможность изменять сетевые запросы крайне опасно.

    Что нужно кэшировать?


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

    • js (vendor, chunks)
    • css
    • медиаконтент (фото, видео, шрифты)

    Почему мы не можем использовать LocalStorage для подобной ситуации?


    Всё очень просто. LocalStorage — синхронный API, имеет ограничение в 5MB и позволяет хранить только строки.

    У SW с этим всё лучше: он асинхронный, является прокси для запросов, что позволяет обрабатывать и кэшировать любой запрос и согласно статье Offline Storage for Progressive Web Apps от Эдди Османи:

    • Chrome/Opera: оба хранилища будут хранить данные, пока не достигнут ограничения браузера. Фактически это безграничное пространство (подробнее в Quota Management API и Persistent Storage).
    • Firefox: нет ограничений, подтверждение после 50MB.
    • Mobile Safari: ограничение в 50MB.
    • Desktop Safari: нет ограничений, подтверждение после 5MB.
    • IE10+: максимум 250MB и подтверждение при 10MB.

    Мне уже нравится Service Worker. Как его использовать?


    Ниже я расскажу про рецепты приготовления SW для создания отзывчивых и понятных приложений.

    Заготовки для приготовления Service Workers


    Для написания своего собственного SW нам понадобятся:

    • index.html
    • index.js
    • sw.js

    Всё, что нужно сделать, это в index.html подключить index.js, в котором будет происходить регистрация файла sw.js

    // Проверка того, что наш браузер поддерживает Service Worker API.
    if ('serviceWorker' in navigator) {
        // Весь код регистрации у нас асинхронный.
        navigator.serviceWorker.register('./sw.js')
          .then(() => navigator.serviceWorker.ready.then((worker) => {
            worker.sync.register('syncdata');
          }))
          .catch((err) => console.log(err));
    }
    

    В файле sw.js нам нужно лишь определить базовые события, на которые будет реагировать SW.

    self.addEventListener('install', (event) => {
        console.log('Установлен');
    });
    
    self.addEventListener('activate', (event) => {
        console.log('Активирован');
    });
    
    self.addEventListener('fetch', (event) => {
        console.log('Происходит запрос на сервер');
    });
    

    Подробности про lifecycle для SW вы можете узнать из данной статьи.

    Рецепт №1 — Network or cache


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


    Решение

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

    Механизм работы следующий: идёт запрос на ресурс с ограничением по времени, например 400ms, если данные не были получены в течении этого времени, мы отдаём их из кэша.

    SW в этом рецепте пытается получить самый актуальный контент из сети, но если запрос занимает слишком много времени, то данные будут взяты из кэша. Эту проблему можно решить путём выставления timeout’а на запрос.

    const CACHE = 'network-or-cache-v1';
    const timeout = 400;
    // При установке воркера мы должны закешировать часть данных (статику).
    self.addEventListener('install', (event) => {
        event.waitUntil(
            caches.open(CACHE).then((cache) => cache.addAll([
                    '/img/background'
                ])
            ));
    });
    
    // при событии fetch, мы и делаем запрос, но используем кэш, только после истечения timeout.
    self.addEventListener('fetch', (event) => {
        event.respondWith(fromNetwork(event.request, timeout)
          .catch((err) => {
              console.log(`Error: ${err.message()}`);
              return fromCache(event.request);
          }));
    });
    
    // Временно-ограниченный запрос.
    function fromNetwork(request, timeout) {
        return new Promise((fulfill, reject) => {
            var timeoutId = setTimeout(reject, timeout);
            fetch(request).then((response) => {
                clearTimeout(timeoutId);
                fulfill(response);
            }, reject);
        });
    }
    
    function fromCache(request) {
    // Открываем наше хранилище кэша (CacheStorage API), выполняем поиск запрошенного ресурса.
    // Обратите внимание, что в случае отсутствия соответствия значения Promise выполнится успешно, но со значением `undefined`
        return caches.open(CACHE).then((cache) =>
            cache.match(request).then((matching) =>
                matching || Promise.reject('no-match')
            ));
    }
    

    Рецепт №2 — Cache only


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


    Решение

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

    const CACHE = 'cache-only-v1';
    
    // При установке воркера мы должны закешировать часть данных (статику).
    self.addEventListener('install', (event) => {
        event.waitUntil(
            caches.open(CACHE).then((cache) => {
                return cache.addAll([
                    '/img/background'
                ]);
            })
        );
    });
    
    // При запросе на сервер (событие fetch), используем только данные из кэша.
    self.addEventListener('fetch', (event) =>
        event.respondWith(fromCache(event.request));
    );
    
    function fromCache(request) {
        return caches.open(CACHE).then((cache) =>
          cache.match(request)
              .then((matching) => matching || Promise.reject('no-match'))
        );
    }
    

    Рецепт №3 — Cache and update


    Данный рецепт решает проблему актуальности данных, чего не было в рецепте №2.
    Иными словами мы получим обновлённый контент, но с задержкой до следующей загрузки страницы.


    Решение

    Как и в предыдущем варианте, в данном рецепте SW сначала отвечает из кэша, чтобы доставить быстрые ответы, но при этом обновляет данные кэша из сети.

    const CACHE = 'cache-and-update-v1';
    
    // При установке воркера мы должны закешировать часть данных (статику).
    self.addEventListener('install', (event) => {
        event.waitUntil(
            caches.open(CACHE).then((cache) =>
                cache.addAll(['/img/background']))
        );
    });
    
    // при событии fetch, мы используем кэш, и только потом обновляем его данным с сервера
    self.addEventListener('fetch', function(event) {
        // Мы используем `respondWith()`, чтобы мгновенно ответить без ожидания ответа с сервера.
        event.respondWith(fromCache(event.request));
        // `waitUntil()` нужен, чтобы предотвратить прекращение работы worker'a до того как кэш обновиться.
        event.waitUntil(update(event.request));
    });
    
    function fromCache(request) {
        return caches.open(CACHE).then((cache) =>
            cache.match(request).then((matching) =>
                matching || Promise.reject('no-match')
            ));
    }
    
    function update(request) {
        return caches.open(CACHE).then((cache) =>
            fetch(request).then((response) =>
                cache.put(request, response)
            )
        );
    }
    

    Рецепт №4 — Cache, update and refresh


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




    Решение

    Рецепт позволяет SW отвечать из кэша, чтобы отдавать быстрые ответы, а также обновлять данные в кэше из сети. Когда запрос выполнится успешно, пользовательский интерфейс будет обновлён автоматически или посредством UI-контрола.

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

    const CACHE = 'cache-update-and-refresh-v1';
    
    // При установке воркера мы должны закешировать часть данных (статику).
    self.addEventListener('install', (event) => {
        event.waitUntil(
            caches
                .open(CACHE)
                .then((cache) => cache.addAll(['/img/background']))
        );
    });
    
    // При запросе на сервер мы используем данные из кэша и только после идем на сервер.
    self.addEventListener('fetch', (event) => {
        // Как и в предыдущем примере, сначала `respondWith()` потом `waitUntil()`
        event.respondWith(fromCache(event.request));
        event.waitUntil(
          update(event.request)
          // В конце, после получения "свежих" данных от сервера уведомляем всех клиентов.
          .then(refresh)
        );
    });
    
    function fromCache(request) {
        return caches.open(CACHE).then((cache) =>
            cache.match(request).then((matching) =>
                matching || Promise.reject('no-match')
            ));
    }
    
    function update(request) {
        return caches.open(CACHE).then((cache) =>
            fetch(request).then((response) =>
                cache.put(request, response.clone()).then(() => response)
            )
        );
    }
    
    // Шлём сообщения об обновлении данных всем клиентам.
    function refresh(response) {
        return self.clients.matchAll().then((clients) => {
            clients.forEach((client) => {
                // Подробнее про ETag можно прочитать тут
                // https://en.wikipedia.org/wiki/HTTP_ETag
                const message = {
                    type: 'refresh',
                    url: response.url,
                    eTag: response.headers.get('ETag')
                };
                // Уведомляем клиент об обновлении данных.
                client.postMessage(JSON.stringify(message));
            });
        });
    }
    

    Рецепт №5 — Embedded fallback


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

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



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


    Решение

    Нужно отдать 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 =
        '<div>\n' +
        '    <div>App Title</div>\n' +
        '    <div>you are offline</div>\n' +
        '    <img src="/svg/or/base64/of/your/dinosaur" alt="dinosaur"/>\n' +
        '</div>';
    
    // Он никогда не упадет, т.к мы всегда отдаем заранее подготовленные данные.
    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 для приложений.
    Они описаны по мере усложнения. Если у вас простой лендинг — не нужно лезть в дебри, просто используйте Cache only или Network or cache. Для более сложных приложений используйте остальные рецепты.

    Статья задумывалась начальной в серии статей о SW API. Хочется понять, насколько тема интересна и полезна. Жду ваших комментариев и пожеланий.

    2ГИС

    148,79

    Карта города и справочник предприятий

    Поделиться публикацией

    Похожие публикации

    Комментарии 35
      0
      Несколько раз пытался разобраться с этими самыми SW, но все не хватало времени. А теперь все вопросы отпали, пойду прикручивать в паре мест.
      0
      Использую гугловский github.com/GoogleChrome/sw-toolbox и .networkFirst политику кэширования.

      Остальные на первый взгляд сладкие варианты кэширования как то .cacheFirst чреваты и требуют многодневного тестирования, и что важно, сложного обслуживания/поддержки.
        0
        Не могу не добавить свои рассуждения по SW:
        1. Поддержка браузеров!

        2. При использовании SW как прокси мы получаем довольно не плохую задержку в загрузке (-5% -5% +25%)
        3. Пуши — почему то многие о них умалчивают когда говорят о SW а тем не менее это довольно крутая штука.

        Как по мне, так использовать SW нужно очень обдуманно. Потому что базовые операции по кешированию отлично могут выполнить заголовки файлов. Отдал заголовок с кешем на месяц, и файл будет браться из кеша браузера. Выкатил новую версию файла, дописал v=number в url. Где здесь SW?
        С оффлайном да, хорошая идея, но опять же много чего в оффлайн не засунешь.
          0
          1. Про поддержку браузеров.
          Как я знаю поддержка в таких браузерах как Safari или Edge находится в разработке.
          Поэтому данная технология довольно-таки перспективна на мой взгляд.
          Ровно как и часть крупных приложений уже начинают использовать их на проде (twitter, tinder, pinterest, smashingmagazine).

          2 и 3
          -5% -5% +25% — как сказал Артём Белов: “… сначала забирает, а потом добавляет с лихвой...”
          По поводу прокси, пушей и как настроить общение между воркером и веб-страницей ещё будет статья. Тут идея была расписать базовые стратегии для кэширования и как их использовать, по сути “на пальцах“.

          Касательно, выставить заголовки и т.п.
          Базовые средства оптимизации никто не отменяет)
          Кешировать надо все, что только возможно (с умом естественно), просто SW может помочь сделать приложение более отзывчивым и ускорить его отображение пользователю.

          По поводу оффлайна:
          А всё ли надо засовывать в оффлайн? На мой взгляд нужно лишь класть критичные файлы (css, js, html) и правильно обрабатывать ситуацию, когда нет доступа к сети, оставляя пользователя в приложении, а не заставляя его ждать минуту и в итоге показать Динозаврика.
            0
            Статья задумывалась начальной в серии статей о SW API. Хочется понять, насколько тема интересна и полезна. Жду ваших комментариев и пожеланий.

            В общем, продолжайте!
              +1
              Спасибо большое)))
              Как говориться, подписывайтесь, ставьте лайки и комментируйте)))
              0

              Все еще непонятно, чем кэширование js/css с помощью SW лучше обычного кэширования на основе заголовков. Можете привести конкретный пример, где решение с SW подходит лучше?

                0

                Отдать закешированный ответ, а в фоне скачать новый для следующего запроса.

                  0

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

                    0
                    Есть конкретный пример, где это имеет смысл?
                    Я чуть-чуть подправил код и для меня в принципе не страшно, если юзер еще один заход будет пользоваться старой версией приложения вместо того, чтобы получить задержку на скачивание нового бандла. В конце концов, я могу показать кнопку «доступна новая версия, обновить страницу».
                      0
                      в принципе не страшно, если юзер еще один заход будет пользоваться старой версией

                      Но бывают же и изменения, которые надо юзеру сразу доставить (например, старая версия кода несовместима с новой разметкой, которую генерит CMS, или с новым API).
                      Как отличать эти "важные" изменения от "неважных", чтобы SW отдавал закэшированную версию в одном случае, но не в другом?


                      И еще такой момент. В реальном приложении нельзя полагаться исключительно на SW — так как есть неподдерживающие его браузеры. Значит, у вас уже предусмотрена система кэширования с возможностью инвалидации кэша при новых релизах. Например, добавляем параметр ?v=< version> к url ресурсов. Или, согласно рекомендации от webpack, дописываем хэш файла, посчитанный при сборке.
                      В результате при изменении кода URL меняется — и SW не найдет измененный файл в своем кэше.


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

                        0
                        SW это программа, которую вы пишете, а не фиксированная модификация поведения. Вы можете запрогарммировать его как хотите. Поэтому
                        В результате при изменении кода URL меняется — и SW не найдет измененный файл в своем кэше.
                        только если вы так запрограммируете. Срезайте ?v=3 в обработчике запроса в коде SW, например.
                        Как отличать эти «важные» изменения от «неважных
                        Тут аналогичный ответ — вы пишете код, так что как хотите. Добавьте параметр &major-version=3 например.
                          0

                          В теории звучит ясно, но дьявол, как всегда, в деталях. Здорово было бы увидеть реальный пример реализации. Ни одной статьи с описанием такого подхода мне не попадалось.


                          Наиболее осмысленное применение кэша в SW из примеров, которые я видел в интернете: пре-кэшинг файлов в связке с HTTP/2 Server Push — для решения проблемы с кэшем, присущей Server Push (например, тут).

                  0
                  В случае кэширования в cacheStorage (не обязательно в service-worker, оно доступно и для window) при отправке запроса на серв не нужно получать заголовок — можно перехватить, проверить на какой ресурс запрос, и отдать соответствующий response из cacheStorage. И все это локально. На клиенте. Потому что раньше предусмотрительно положили туда нужные пары request-respones. Поэтому если клиент оффлайн, pwa'шка может отдавать соответствующий response без обращения к серверу. В этом и суть оффлайн pwa. Запросы не уходят в сеть. А чтобы получить заголовок кэширования обычными методами серва, нужна сеть.
                    0
                    А чтобы получить заголовок кэширования обычными методами серва, нужна сеть.

                    Неправда. Если файл уже закэширован браузером после первого запроса, то последующие запросы к нему выполняются без сети. Я имею в виду безусловное кэширование типа Cache-Control:max-age=<много-много секунд> (в отличие от условного кэширования с ETag/If-Modified-Since).


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

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

                      Ключевое здесь файл. Статика. В случае кэширования в cacheStorage можно положить text/html response. При чем положить заранее, при первом обращении к серверу. При чем respone на любой request.url. То есть фактически при первом обращении задать список доступных оффлайн страниц.
                        0
                        В случае кэширования в cacheStorage можно положить text/html response

                        Да, про такой сценарий я не подумал. Обычно саму страницу никто "жестко" (с Cache-Control: max-age) кэшировать не станет. В этом случае SW позволит сделать фоллбэк на оффлайн-режим при отсутствии сети.
                        Хотя лично мне кажется, что оффлайн режим имеет очень ограниченную область применения. Имхо, не так много реальных приложений, в которых можно сделать что-то осмысленное без сети (типа написания черновика письма в gmail).


                        То есть фактически при первом обращении задать список доступных оффлайн страниц.

                        Ясно, pre-caching. Причем, "доступных оффлайн" — здесь даже необязательная часть. Сама страница может требовать сети для работы, но мы все еще можем заранее для нее что-то подгрузить.

                          0
                          Обычно саму страницу никто «жестко» (с Cache-Control: max-age) кэшировать не станет
                          Зависит от сценария, который реализуется. Как правило при первом обращении кэшируется вся статика необходимая для работы сайта/приложения и какой-то список страниц: rg.ru последние N новостей и показывает плашку «Ой! У вас нет интернета.», видел магазин который клал в кэш сразу все инфо страницы, сайт рекламного агентства — страницы оферов и их описаний.
              0
              Почему-то про сервис-воркеры пишут только в контексте кэша и работы оффлайн.

              Использую для выполнения фоновых операций, +push уведомления юзерам о статусе этих операций и возможном прекращении их выполнения(если видно что закрыты все вкладки).
              Как результат например общая для всех вкладок область видимости/«глобальная асинхронность», то есть если юзер открыл сервис в нескольких вкладках он не может запустить занового идентичный процесс, т.к. этот процесс идет в едином сервис-воркере.

                0
                Да, в курсе про такие операции)
                Это очень классная вещь)
                Я хотел начать с базовых вещей, а далее, в последующих статьях хочется погрузиться в те же пуши, фоновые операции и в другие интересные вещи)
                0
                Простите, я возможно отстал от жизни, но неужели SW стал по настоящему доступен на всех устройствах, помнится пол года назад с ним были явные проблемы на устройствах Apple.
                  0
                  Про поддержку браузеров можете посмотреть в этом комменте
                  habrahabr.ru/company/2gis/blog/345552/#comment_10586162

                  О каких именно проблемах вы говорите? Поддержки сейчас нет в Safari — это правда, но как минимум SW в разработке для Safari и Edge.
                  0
                  Нужна статья о синхронизации и фоновом обновления кэша. Например когда используется cacheFirst и кэшируется вообще все. Статику понятно, можно версионированием кэшей контролировать, а вот с контентом пока только костыли использовал. Например пользователь из листинга ушел в карточку товара, там добавил его в корзину, возвращается на листинг, а ему отдает версия страницы из кэша в которой корзина пустая. Делал для этого historyArr[] в котором хранил все request.url и при определенном запросе на сервер обновлял contentCache чтобы контент всегда был актуальный, но так как немношк не разраб, чувствую что костыль не оч верный и есть нативная синхронизация для подобных вещей.
                    0
                    как я понимаю из вашего контекста, данная задача мало связана с SW.
                    Если мы говорим о SPA, то подобную задачу можно решить через тот же redux и управлением данными через store, то есть вы сами контролируете, какой листинг вы открыли и если есть данные в store, то запрос можно не делать, а брать данные прямиком из хранилища.
                    0
                    Нет, не SPA, обычное PWA которое прикручивается к обычному интернет-магазину с обычным php на бэкенде. Контент в SW делится на imgCache, staticCache и contentCache.

                    Во время сёрфинга по сайту в contentCache пишется text/html и соответственно при cacheFirst отдается из кэша. Так вот именно его нужно обновлять при определенных действиях: добавили в корзину, удалил, добавил в избранное, к сравнению, лайк\дизлайк, оставил комментарий. Потому что он хранит контент до этих действий. Надеюсь так понятнее :)

                    И вот такой костыль решает задачу, но чувствую что есть нативное решение.
                    function updateCacheByName (cacheName,updateRequestsArray) {
                    caches.delete(cacheName)
                    .then(function(){
                    console.log('удалили кэш '+cacheName+' чтобы обновить');
                    caches.open(cacheName).then(cache => {
                    return cache.addAll(updateRequestsArray).then(function(){
                    console.log('история обновлена в кэше '+cacheName);
                    }).catch( err => {console.log(err);});
                    });
                    }).catch( err => {console.log(err);});
                    }
                      0
                      Я ответил Вам в личку, чтобы не понижать контекст статьи и в целом ее обсуждения)
                      0

                      Мне нравятся статьи, в которых последовательно наращивается уровень материала.


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


                      В общем, я за продолжение.

                        0
                        В-третьих, из соображений безопасности SW работают только по HTTPS

                        Это не правда, есть возможность динамически создавать worker
                        Динамический worker
                            function getWorker() {
                                var blob;
                                window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
                                var mimeString = 'application/javascript';
                                var code = "onmessage = function(e) { postMessage('My' + e.data); }";
                                try {
                                    blob = new Blob([code], {type: mimeString});
                                } catch (e) {
                                    var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
                                        window.MozBlobBuilder || window.MSBlobBuilder;
                                    var bb = new BlobBuilder();
                                    bb.append(code);
                                    blob = bb.getBlob(mimeString);
                                }
                                var url = window.URL.createObjectURL(blob);
                                var worker = new Worker(url);
                                URL.revokeObjectURL(url);
                                return worker;
                            }
                        
                            var worker = getWorker();
                            worker.postMessage('Test');
                            worker.onmessage = function (e) {
                                console.log(e.data);
                            };
                        


                        давать посторонним людям возможность изменять сетевые запросы крайне опасно

                        Можете пояснить ход ваших мыслей здесь. Откровенно подумал, что очередной перевод
                        Полностью согласен с комментарием
                          0
                          P. S. Позже увидел, что спутал serviceWorker с Worker
                          0
                          Недавно, один популярный web-сервис внедрил подход «offline first» (или как он там правильно называется). Так вот, однажды при использовании я не сразу заметил, что интернета нет, и продолжал пользоваться сервисом. При этом, несколько раз ловил дикий «ux confusing», если можно так выразится. И только потом заметил маленькую системную надпись от браузера (рядом с протоколом в адресной строке) — типа «сайт offline», или как-то так. Я был несколько расстроен, потому что понял что терял время и контент был невалидный, а взаимодействие фейковое. Ещё подумал про себя — «началось, начали тестить продвинутое кеширование и сервис-воркеры». Так это я разобрался, а что говорить про людей далёких от web-разработки.

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

                          как по мне, так это не проблема, а вполне понятное, чёткое и однозначное информирование о том что интернета пока нет, займись чем-то другим!
                          Моё мнение, что для сервисов, где есть плотное взаимодействие с пользователем, или контентных сервисов — подход «offline first» неудачный. Он годится лишь для чисто клиентских самодостаточных приложений (типа как тот гудящий горн на google developers, который приводят везде в пример)

                          p.s. хотя подозреваю, я тут напутал и намешал с pwa..))
                            0
                            Тут на самом деле понамешано малость.
                            Да, PWA это про SW, но SW — это не про PWA.

                            > как по мне, так это не проблема, а вполне понятное, чёткое и однозначное информирование о том что интернета пока нет, займись чем-то другим!

                            Как я вижу в плане дизайна приложения был допущена ошибка, что пользователь явно не показал, что вы в Offline. По мне это решается довольно просто:
                            window.addEventListener('offline', () => showOfflineBar());
                            

                            Это уже проблема приложения, а не SW. Как я выше писал в своей статье, наша задача не просто не показывать «динозаврика», а показать своего «динозаврика» в стиле нашего приложения и была «наша» кнопка «обновить».
                            0
                            Уточните пожалуйста, разве XHR стали синхронными по умолчанию? Также интересно узнать, какая проблема обернуть синхронный вызов localStorage в колбеки, промисы, генераторы – на выбор? Но прежде чем оборачивать вызов localStorage в асинхронный вызов, я бы хотел понять, зачем? Если максимальный размер хранилища 5Мб, то сколько же по вашему мнению будет извлечение этого объёма из оперативы в синхронном режиме? Наберется ли там хотя бы микросекунда?

                            Одним словом, вы не спутали здесь синхронное исполнение кода и вызов, блокирующий процесс?

                            Я ничего против воркеров и сервис-воркеров не имею, просто почему синхронность стала проблемой?
                              0
                              Почему никто не пишет так:
                              if (!('serviceWorker' in navigator)){ return; } 
                                +1
                                Сама технология хороша, но с чего Вы решили, что Ваш сайт достоин кеширования у меня на компьютере? Сейчас это 1-2 сайта, потом 100+, зачем мне 5000+ мб шлака, да ещё синхронизирующегося… Не говоря уже о том, что SW жрут батарейку. Меня уже порядком достали сайты, которые хотят присылать мне уведомления или определить местоположения, хотя я к ним зашёл первый раз по ошибке гугла…

                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                Самое читаемое