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

У меня есть прогрессивное веб-приложение, в котором сервис-воркер настроен, как показано ниже. Я следую методу предварительного кэширования. Все файлы будут кэшироваться первыми, а запросы будут обслуживаться из кеша. Если в локальном кеше совпадений нет, то запрос обслуживается по сети. Если ничего не получается, отображается страница офлайн/ошибка. Все работает нормально. Но я застрял в обновлении файла index.html.

const pb_cache = "cv1";
const assets = [
    "./manifest.json",
    "./index.html",
    "./offline.html",
]

self.addEventListener("install", installEvent => {
  installEvent.waitUntil(
    caches.open(pb_cache)
    .then((cache) => {
      return cache.addAll(assets)
      .then(() => {
        return self.skipWaiting(); //To forces the waiting service worker to become the active service worker
      })
    })
  );
});

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      if (response) {
        return response;
      }
      return fetch(event.request).then(function(response) {
        if (response.status === 404) {
          return caches.match('/offline.html');
        }
        return response
      });
    }).catch(function() {
      return caches.match('/offline.html');
    })
  );
});

Сценарий Я установил веб-приложение на свой телефон Android. Все кешируется и работает нормально. Мне нужно внести изменения в файл index.html. Поэтому я добавил несколько настроек в файл и обновил веб-сайт. Но поскольку веб-приложение, установленное в Android, обслуживает локальный кеш, изменение веб-сайта не отражается. Поэтому мне нужен механизм для проверки обновлений. Какой параметр или что я должен проверить на наличие обновлений? Я прочитал много документов, связанных с этим, и я не могу понять содержание.

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


person melvin    schedule 23.02.2021    source источник


Ответы (2)


Ручной способ сделать это - включить что-то вроде

// VERSION: 1

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

После некоторых разъяснений в комментариях я считаю, что использование cache.addAll() внутри вашего обработчика install может быть проблематичным, потому что вы не имеете контроля над своими заголовками Cache-Control, а cache.addAll() отправится в кэш HTTP перед выходом в сеть. Вот альтернативный обработчик install, который решает эту проблему, передавая объекты Request с соответствующим свойством cache вместо передачи строк URL:

self.addEventListener("install", installEvent => {
  const cacheBypassRequests = assets.map(
    (url) => new Request(url, {cache: 'reload'});

  installEvent.waitUntil(
    caches.open(pb_cache)
    .then((cache) => {
      return cache.addAll(cacheBypassRequests)
      .then(() => {
        return self.skipWaiting();
      })
    })
  );
});

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

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

Более готовый к работе подход – использовать инструмент, предназначенный для решения этого конкретного случая использования, например workbox-precaching вместе с интерфейсами сборки node, webpack или CLI. Это позаботится об автоматическом создании хэша каждого ресурса, который вы хотите предварительно кэшировать, инициировании новой установки при каждом изменении одного из них и повторной загрузке только обновленных ресурсов.

person Jeff Posnick    schedule 23.02.2021
comment
Ok. Если я использую первый метод, который подвержен ошибкам, решает ли очистка предыдущего кэша перед обновлением текущего проблемы с кэшированием? - person melvin; 23.02.2021
comment
cache.addAll() перезапишет предыдущие записи тем, что было получено из сети, поэтому вам не нужно предварительно ничего явно очищать. Но вам нужно не забыть обновить строку версии вручную, и вам нужно убедиться, что ваши заголовки HTTP Cache-Control не приводят к получению ранее кэшированного ответа, т.е. убедитесь, что ваши заголовки кэширования позволяют вам перейти напрямую к сети. - person Jeff Posnick; 23.02.2021
comment
Я не так уж хорошо разбираюсь в заголовках HTTP-кеша. Я размещаю этот код на github. Так не могли бы вы объяснить это? - person melvin; 23.02.2021
comment
Также что лучше? Добавлять // VERSION: 1 или обновлять имя кеша каждый раз, когда вносятся изменения? - person melvin; 23.02.2021
comment
Я бы обновил значение VERSION, если только вы не очищаете старые имена кеша в обработчике activate. К сожалению, у вас нет контроля над заголовками Cache-Control: на страницах GitHub. Последнее, что я проверял, страницы GitHub будут использовать кеш HTTP в течение 10 минут, что может означать, что ваш вызов cache.addAll() может в конечном итоге попасть в кеш HTTP, а не в сеть. - person Jeff Posnick; 23.02.2021
comment
Таким образом, использование значения VERSION устраняет проблему кэширования, упомянутую выше в комментарии? - person melvin; 23.02.2021
comment
Нет, это не обойдет кеш HTTP. Однако я могу изменить свой исходный ответ, чтобы показать альтернативу cache.addAll(), которая будет обходить кеш HTTP. - person Jeff Posnick; 23.02.2021
comment
Это было бы очень полезно. Я действительно застрял на 2 дня. Я много искал и с трудом понимаю эти вещи. - person melvin; 23.02.2021
comment
У меня есть еще один вопрос. Должен ли я задать его как новый вопрос или задать его здесь, в комментариях? - person melvin; 23.02.2021
comment
Новый вопрос, пожалуйста :-) - person Jeff Posnick; 23.02.2021
comment
Этот ответ работает. Большое спасибо за помощь. не могли бы вы взглянуть на это? stackoverflow.com/q/66338795/8913606 - person melvin; 23.02.2021
comment
Привет. После предоставления нескольких обновлений, как вы предложили, используя номера версий, установленный pwa app не перезаписывает кеш. Когда изменение обновляется на веб-сайте, соответствующее не обновляется в приложении. Мне нужно очистить кеш браузера Google Chrome на мобильном устройстве и снова загрузить приложение, чтобы обновить кеш. Не могли бы вы знать, почему. Я не добавил ничего, кроме приведенного выше кода. - person melvin; 27.02.2021

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

Вот решение с использованием Workbox, оно предварительно кэширует файлы, указанные в urls, затем, когда будет сделан запрос на один из этих файлов, он будет использовать ссылку устарел, при повторной проверке стратегии. Сначала пользователь может увидеть старую версию, но при следующей перезагрузке он получит последнюю версию. Для любых других запросов (отсутствующих в urls) будет использоваться только сеть. И, наконец, setCatchHandler< /a> вернет автономную страницу, если не сможет получить страницу из сети.

importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.1.0/workbox-sw.js');

// URLs to cache and keep up to date
const urls = [
  '/index.html',
  '/manifest.json',
  '/script.js',
  '/style.css',
  '/offline.html',
];

// Turn on logging for development, change to false for production
workbox.setConfig({
  debug: true
});

const {clientsClaim} = workbox.core;
const {NetworkOnly} = workbox.strategies;
const {StaleWhileRevalidate} = workbox.strategies;
const {warmStrategyCache} = workbox.recipes;
const {registerRoute} = workbox.routing;
const {setDefaultHandler} = workbox.routing;
const {setCatchHandler} = workbox.routing;

self.skipWaiting();
clientsClaim();

// Normalize cache key URLs to:
// - drop query parameters
// - for URLs ending in '/', append 'index.html'
async function cacheKeyWillBeUsed({request}) {
  const url = new URL(request.url);
  if (url.pathname.endsWith('/')) {
    return url.origin + url.pathname + 'index.html';
  }
  return url.origin + url.pathname;
}

// Initialize a stale while revalidate strategy.
// See https://developers.google.com/web/tools/workbox/modules/workbox-strategies#stale-while-revalidate
const strategy = new StaleWhileRevalidate({
  plugins:[
    {cacheKeyWillBeUsed},
  ],
});

// Ensure that an initial set of URLs are cached,
// so that the PWA works offline immediately.
warmStrategyCache({urls, strategy});

// Use the Stale While Revalidate strategy for URLs in `urls`
registerRoute(
  ({url}) => {
    let pathname = url.pathname;
    // Normalize paths, for URLs ending in '/', append 'index.html'
    if (pathname.endsWith('/')) {
      pathname += 'index.html';
    }
    return urls.includes(pathname);
  }, strategy
);

// Use only the network for all other requests
setDefaultHandler(new NetworkOnly());

// This "catch" handler is triggered when any of the other routes fail to
// generate a response. This is a simplified version of the Comprehensive Fallback
// https://developers.google.com/web/tools/workbox/guides/advanced-recipes#comprehensive_fallbacks
setCatchHandler(({event}) => {
  if (event.request.destination === 'document') {
    return caches.match('/offline.html');
  }
});

Все это решение объединяет ряд рецептов из Расширенные рецепты. раздел Рабочего ящика.

person PeteLe    schedule 23.02.2021
comment
Здесь уместно изменить имя кеша. Я рассмотрю варианты рабочего ящика позже. Лучше перезаписать предыдущий кеш или удалить и добавить как новый кеш? - person melvin; 23.02.2021
comment
Удалить и добавить новый кеш чуть лучше. - person PeteLe; 23.02.2021
comment
Ok. Как упомянул другой человек в другом ответе, добавление // ВЕРСИИ: 1 в рабочий файл или обновление имени кеша каждый раз, когда вносятся изменения? Как лучше ? - person melvin; 23.02.2021
comment
Спасибо за вашу помощь. Большое спасибо. У меня есть примерное представление о том, как это сделать :) - person melvin; 23.02.2021