1

Я пытаюсь использовать «Pre-fetching» и извлекать методы «собрать» для кэширования js, CSS и материалов в SPA-приложении.Как совместить служебного работника Предварительная выборка с помощью навигации по навигации?

Чтобы упреждающее чтение сценариев я попробовал код очень нравится этот фрагмент кода:

self.addEventListener('install', function(event) { 
 
     var now = Date.now(); 
 
    
 
     var urlsToPrefetch = [ 
 
     'static/pre_fetched.txt', 
 
     'static/pre_fetched.html' 
 
     ]; 
 
    
 
      
 
     event.waitUntil(
 
     caches.open(CURRENT_CACHES.prefetch).then(function(cache) { 
 
      var cachePromises = urlsToPrefetch.map(function(urlToPrefetch) { 
 
      var url = new URL(urlToPrefetch, location.href); 
 
      url.search += (url.search ? '&' : '?') + 'cache-bust=' + now; 
 
      var request = new Request(url, {mode: 'no-cors'}); 
 
      return fetch(request).then(function(response) { 
 
       if (response.status >= 400) { 
 
       throw new Error('request for ' + urlToPrefetch + 
 
        ' failed with status ' + response.statusText); 
 
       } 
 
    
 
       return cache.put(urlToPrefetch, response); 
 
      }).catch(function(error) { 
 
       console.error('Not caching ' + urlToPrefetch + ' due to ' + error); 
 
      }); 
 
      }); 
 
    
 
      return Promise.all(cachePromises).then(function() { 
 
      console.log('Pre-fetching complete.'); 
 
      }); 
 
     }).catch(function(error) { 
 
      console.error('Pre-fetching failed:', error); 
 
     }) 
 
    ); 
 
    });

Полный код можно проверить here

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

self.addEventListener('fetch', function (event) { 
 
    if (event.request.method === "GET" && testes_to_know_if_it_area_a_js_or_css) { 
 
     event.respondWith(
 
       caches.match(event.request) 
 
       .then(function (response) { 
 
        if (response) { 
 
         loggger && console.log('From Cache', event.request.url); 
 
         return response; 
 
        } 
 

 
        // IMPORTANT: Clone the request. A request is a stream and 
 
        // can only be consumed once. Since we are consuming this 
 
        // once by cache and once by the browser for fetch, we need 
 
        // to clone the response 
 
        var fetchRequest = event.request.clone(); 
 

 
        return fetch(fetchRequest).then(
 
          function (response) { 
 
           // Check if we received a valid response 
 
           if (!response || response.status !== 200 || response.type !== 'basic') { 
 
            return response; 
 
           } 
 

 
           // IMPORTANT: Clone the response. A response is a stream 
 
           // and because we want the browser to consume the response 
 
           // as well as the cache consuming the response, we need 
 
           // to clone it so we have 2 stream. 
 
           var responseToCache = response.clone(); 
 

 
           caches.open(CURRENT_CACHES['general-cache']) 
 
             .then(function (cache) { 
 
              try { 
 
               loggger && console.log('Add to Cache', event.request.url); 
 
               cache.put(event.request, responseToCache); 
 
              } catch (e) { 
 
               console.error(e); 
 
              } 
 
             }); 
 

 
           return response; 
 
          } 
 
        ); 
 
       }) 
 
       ); 
 
    } 
 
});
К сожалению, я не нашел оригинальный сценарий, который я на основе построить этот.

Оба, работают очень хорошо, но не так, как ожидалось. Вторая выборка снова добавляет его в кеш, я думаю, это потому, что caches.match(event.request) не соответствует действительности. Итак, я помещал консоль, чтобы видеть как объекты запроса, так и синтез, созданный с помощью предварительной выборки и клонированный из выборки.

  • Синтетические: enter image description here
  • клонированные: enter image description here

Таким образом, я не уверен, если я могу переписать эти свойства синтетического такие же, как клонировать, я могу это сделать безопасно ? Как я могу это решить?

PS: Этот код не запускается как обычные скрипты. Фрагмент был просто организован.

ответ

1

Я не нашёл ссылок для подтверждения своего решения, но его работы.

Решения было создать 2 различные кэша и «Нормализация» запрос клонирование в синтетический запрос, удалив все ссылки, сохраняя только основные:

var CURRENT_CACHES = { 
    'prefetch-cache': 'prefetch-cache-v' + CACHE_VERSION, //Prefetch cach 
    'general-cache': 'general-cache-v' + CACHE_VERSION, 
}; 

prefetch-cache отвечает хранить все файлы, которые Я хочу выполнить предварительную выборку у моего сервисного работника, а общий кеш - для всех других файлов (имеет смысл, когда у вас есть СПА и вы хотите скопировать некоторые запросы, такие как файлы перевода, компоненты js, css и другие материалы).

Вы можете создать массив с URI всех файлов, которые вы хотите упреждение:

var urlsToPrefetch = [ 
    //JS 
    "plugin/angular/angular.min.js", "plugin/requirejs/require.min.js","app/main.js","app/app.js","app/includes.js" 
    //CSS 
    ,"styles/css/print.css","styles/css/bootstrap.css","styles/css/fixes.css", 
    //Html 
    ,"app/layout/partials/menu.tpl.html", "app/layout/public.tpl.html", 
    //JSON 
    ,"app/i18n/languages.json","app/i18n/pt-br.json", "app/i18n/en.json" 
]; 

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

self.addEventListener('install', function (event) { 
    logger && console.log('Handling install event:', event); 

    //var now = Date.now(); 

    // All of these logging statements should be visible via the "Inspect" interface 
    // for the relevant SW accessed via chrome://serviceworker-internals 
    if (urlsToPrefetch.length > 0) { 
     logger && console.log('Handling install event. Resources to prefetch:', urlsToPrefetch.length , "resources"); 
     event.waitUntil(
       caches.open(CURRENT_CACHES['prefetch-cache']).then(function (cache) { 
      var cachePromises = urlsToPrefetch.map(function (urlToPrefetch) { 
       urlToPrefetch += '?v=' + CACHE_VERSION; 
       // This constructs a new URL object using the service worker's script location as the base 
       // for relative URLs. 
       //var url = new URL(urlToPrefetch + '?v=' + CACHE_VERSION, location.href); 
       var url = new URL(urlToPrefetch, location.href); 
       // Append a cache-bust=TIMESTAMP URL parameter to each URL's query string. 
       // This is particularly important when precaching resources that are later used in the 
       // fetch handler as responses directly, without consulting the network (i.e. cache-first). 
       // If we were to get back a response from the HTTP browser cache for this precaching request 
       // then that stale response would be used indefinitely, or at least until the next time 
       // the service worker script changes triggering the install flow. 
       //url.search += (url.search ? '&' : '?') + 'v=' + CACHE_VERSION; 

       // It's very important to use {mode: 'no-cors'} if there is any chance that 
       // the resources being fetched are served off of a server that doesn't support 
       // CORS (http://en.wikipedia.org/wiki/Cross-origin_resource_sharing). 
       // In this example, www.chromium.org doesn't support CORS, and the fetch() 
       // would fail if the default mode of 'cors' was used for the fetch() request. 
       // The drawback of hardcoding {mode: 'no-cors'} is that the response from all 
       // cross-origin hosts will always be opaque 
       // (https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cross-origin-resources) 
       // and it is not possible to determine whether an opaque response represents a success or failure 
       // (https://github.com/whatwg/fetch/issues/14). 
       var request = new Request(url, {mode: 'no-cors'}); 
       return fetch(request).then(function (response) { 
        logger && console.log('Add to Cache (Prefetch)', url.href); 


        if (!response || response.status !== 200 || response.type !== 'basic') { 
         throw new Error('request for ' + urlToPrefetch + 
           ' failed with status ' + response.statusText); 
        } 
        //var responseToCache = response.clone(); 

        // Use the original URL without the cache-busting parameter as the key for cache.put(). 
//     return cache.put(urlToPrefetch, responseToCache); 
        return cache.put(urlToPrefetch, response); 
       }).catch(function (error) { 
        logger && console.error('Not caching ' + urlToPrefetch + ' due to ' + error); 
       }); 
      }); 

      return Promise.all(cachePromises).then(function() { 
       logger && console.log('Pre-fetching complete.'); 
      }); 
     }).catch(function (error) { 
      logger && console.error('Pre-fetching failed:', error); 
     })); 
    } 

    // Perform install steps 
// if (urlsToPrefetch.length > 0) { 
//  event.waitUntil(
//    caches.open(CURRENT_CACHES['perma-cache']) 
//    .then(function (cache) { 
//     return cache.addAll(urlsToPrefetch); 
//    }) 
//    ); 
// } 
    // `skipWaiting()` forces the waiting ServiceWorker to become the 
    // active ServiceWorker, triggering the `onactivate` event. 
    // Together with `Clients.claim()` this allows a worker to take effect 
    // immediately in the client(s). 
    return self.skipWaiting(); 
}); 

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

self.addEventListener('fetch', function (event) { 
    //console.log(event); 
    if (event.request.method === "GET") { 
     var qSFilter = "" + ((event.request.url).split('?'))[0];//Filtrar Quetry Stirng 
     //console.log(event.request.url, qSFilter, qSFilter.split(CACHE_SCOPE), CACHE_SCOPE); 
     var leUrl = (qSFilter.split(CACHE_SCOPE))[1]; 
     //Is possible to implement some logic to skip backend calls and other uncachable calls 
     if (/^(app|style|plugin).*(js|css|html|jpe?g|png|gif|json|woff2?)$/.test(leUrl) 
       || /^backend\/server\/file\/i18n\/((?!client).+)\//.test(leUrl) 
       || /^backend\/server\/static\/images\/.*$/.test(leUrl) 
       || /^backend\/server\/static\/style.*$/.test(leUrl) 
       ) { 
      var url = new URL(leUrl + '?v=' + CACHE_VERSION, location.href); 
      var synthetic = new Request(url, {mode: 'no-cors'}); 
      //console.log(event.request,response.clone(),synthetic); 
      event.respondWith(
//     caches.match(event.request) 
        caches.match(synthetic) 
        .then(function (response) { 
         // Cache hit - return response 
         if (response) { 
          logger && console.log('From Cache', event.request.url); 
          return response; 
         } 

         // IMPORTANT: Clone the request. A request is a stream and 
         // can only be consumed once. Since we are consuming this 
         // once by cache and once by the browser for fetch, we need 
         // to clone the response 
         var fetchRequest = event.request.clone(); 

         return fetch(fetchRequest).then(
           function (response) { 
            // Check if we received a valid response 
            if (!response || response.status !== 200 || response.type !== 'basic') { 
             return response; 
            } 

            // IMPORTANT: Clone the response. A response is a stream 
            // and because we want the browser to consume the response 
            // as well as the cache consuming the response, we need 
            // to clone it so we have 2 stream. 
            var responseToCache = response.clone(); 

            caches.open(CURRENT_CACHES['general-cache']) 
              .then(function (cache) { 
               try { 
                logger && console.log('Add to Cache', event.request.url, qSFilter,leUrl); 
                cache.put(event.request, responseToCache); 
               } catch (e) { 
                console.error(e); 
               } 
              }); 

            return response; 
           } 
         ); 
        }) 
        ); 
     } 
    } 
}); 

Полный рабочий сценарий можно получить здесь: https://gist.github.com/LeonanCarvalho/0527526a6b784b23facf56fa3cc12d22

Смежные вопросы