Çevrimdışı Yemek Kitabı

Jake Archibald
Jake Archibald

Service Worker sayesinde çevrimdışı çözüm üretmeyi bıraktık ve geliştiricilere, sorunu kendi kendilerine çözmeleri için gereken hareketli parçaları verdik. Önbelleğe alma ve isteklerin nasıl işlendiği üzerinde kontrol sahibi olmanızı sağlar. Yani kendi desenlerinizi oluşturabilirsiniz. Olası birkaç kalıba tek tek göz atalım. Ancak pratikte, URL'ye ve bağlama bağlı olarak bunların çoğunu birlikte kullanmanız muhtemeldir.

Bu kalıplardan bazılarının çalışan bir demosu için Heyecan odaklı eğitim videosunu izleyebilir ve performans üzerindeki etkiyi gösteren bu videoyu izleyebilirsiniz.

Önbelleğe alma makinesi: Kaynakları ne zaman depolayacağınız

Hizmet Çalışanı, istekleri önbelleğe alma işleminden bağımsız olarak işlemenize olanak tanır. Bu nedenle, bunları ayrı ayrı göstereceğim. Öncelikle, önbelleğe alma işlemi ne zaman yapılmalıdır?

Yükleme sırasında (bağımlılık olarak)

Yüklemede: Bağımlılık olarak.
Yükleme sırasında (bağımlı olarak).

Service Worker size bir install etkinliği verir. Diğer etkinlikleri gerçekleştirmeden önce bazı öğeleri hazır hale getirmek için bunu kullanabilirsiniz. Bu işlem sırasında Hizmet İşleyicinizin önceki sürümleri çalışmaya ve sayfa yayınlamaya devam eder. Bu nedenle, burada yaptığınız işlemler bu durumu etkilememelidir.

Kullanım alanları: CSS, resimler, yazı tipleri, JS, şablonlar... temel olarak sitenizin "sürümüne" göre statik olarak kabul edebileceğiniz her şey.

Bunlar getirilemezse sitenizin tamamen işlevsel olmamasına neden olan şeylerdir. Platforma özgü eşdeğer bir uygulama, ilk indirme işleminin bir kısmını oluşturur.

self.addEventListener('install', function (event) {
  event.waitUntil(
    caches.open('mysite-static-v3').then(function (cache) {
      return cache.addAll([
        '/css/whatever-v3.css',
        '/css/imgs/sprites-v6.png',
        '/css/fonts/whatever-v8.woff',
        '/js/all-min-v4.js',
        // etc.
      ]);
    }),
  );
});

event.waitUntil, yüklemenin uzunluğunu ve başarısını tanımlamak için bir söz alır. Sözleşme reddedilirse yükleme başarısız kabul edilir ve bu Hizmet Çalışanı terk edilir (çalışır durumdaki eski bir sürüm olduğunda ise bu sürüm olduğu gibi bırakılır). caches.open() ve cache.addAll() iade sözü. Kaynaklardan herhangi biri getirilemezse cache.addAll() çağrısı reddedilir.

training-to-thrill aracında statik öğeleri önbelleğe almak için bunu kullanıyorum.

Yükleme sırasında (bağımlılık olarak değil)

Yükleme sırasında (bağımlılık olarak değil).
Yükleme sırasında (bağımlılık olarak değil).

Bu, yukarıdakine benzer ancak yüklemenin tamamlanmasını geciktirmez ve önbelleğe alma başarısız olursa yüklemenin başarısız olmasına neden olmaz.

İdeal kullanım alanı: Bir oyunun sonraki seviyelerine yönelik öğeler gibi hemen ihtiyaç duyulmayan daha büyük kaynaklar.

self.addEventListener('install', function (event) {
  event.waitUntil(
    caches.open('mygame-core-v1').then(function (cache) {
      cache
        .addAll
        // levels 11–20
        ();
      return cache
        .addAll
        // core assets and levels 1–10
        ();
    }),
  );
});

Yukarıdaki örnekte, 11-20 arasındaki seviyeler için cache.addAll vaadi event.waitUntil'a iletilmemiştir. Bu nedenle, oyun başarısız olsa bile çevrimdışı olarak oynanmaya devam eder. Elbette bu düzeylerin bulunmama olasılığını göz önünde bulundurmanız ve eksiklerse bunları önbelleğe almaya yeniden çalışmanız gerekir.

11-20 arasındaki seviyeler indirilirken Service Worker, etkinlikleri işlemeye son verdiği için sonlandırılabilir. Bu durumda, seviyeler önbelleğe alınmaz. Gelecekte Web Periodic Background Synchronization API, bu tür durumların yanı sıra filmler gibi daha büyük indirme işlemlerini de yönetecek. Bu API şu anda yalnızca Chromium çatallarında desteklenmektedir.

Etkinleştirildiğinde

Etkinleştirildiğinde.
Etkinleştirildiğinde

Kullanım alanları: Temizlik ve taşıma.

Yeni bir Hizmet Çalışanı yüklendikten ve önceki sürüm kullanılmadığında yeni sürüm etkinleştirilir ve bir activate etkinliği alırsınız. Eski sürüm kullanımdan kaldırıldığı için IndexedDB'de şema taşıma işlemlerini gerçekleştirmek ve kullanılmayan önbellekleri silmek için iyi bir zaman.

self.addEventListener('activate', function (event) {
  event.waitUntil(
    caches.keys().then(function (cacheNames) {
      return Promise.all(
        cacheNames
          .filter(function (cacheName) {
            // Return true if you want to remove this cache,
            // but remember that caches are shared across
            // the whole origin
          })
          .map(function (cacheName) {
            return caches.delete(cacheName);
          }),
      );
    }),
  );
});

Etkinleştirme sırasında fetch gibi diğer etkinlikler sıraya alınır. Bu nedenle, uzun bir etkinleştirme işlemi sayfa yüklenmesini engelleyebilir. Etkinleştirmenizi mümkün olduğunca basit tutun ve yalnızca eski sürüm etkinken yapamadığınız işlemler için kullanın.

trained-to-thrill kanalında bu özelliği eski önbellekleri kaldırmak için kullanıyorum.

Kullanıcı etkileşimi sırasında

Kullanıcı etkileşimi üzerine.
Kullanıcı etkileşimi üzerine.

Kullanım alanı: Sitenin tamamı çevrimdışı alınamadığında ve kullanıcının çevrimdışı kullanmak istediği içeriği seçmesine izin verdiğinizde. Örneğin, YouTube gibi bir platformdaki bir video, Wikipedia'daki bir makale veya Flickr'daki belirli bir galeri.

Kullanıcıya "Daha sonra oku" veya "Çevrimdışı kullanım için kaydet" düğmesi verin. Tıklandığında, ağdan ihtiyacınız olan bilgileri getirip önbelleğe yerleştirin.

document.querySelector('.cache-article').addEventListener('click', function (event) {
  event.preventDefault();

  var id = this.dataset.articleId;
  caches.open('mysite-article-' + id).then(function (cache) {
    fetch('/get-article-urls?id=' + id)
      .then(function (response) {
        // /get-article-urls returns a JSON-encoded array of
        // resource URLs that a given article depends on
        return response.json();
      })
      .then(function (urls) {
        cache.addAll(urls);
      });
  });
});

Önbellekler API'si, hem sayfalarda hem de hizmet çalışanlarında kullanılabilir. Yani önbelleğe doğrudan sayfadan ekleme yapabilirsiniz.

Ağ yanıtında

Ağ yanıtında.
Ağ yanıtında.

Kullanım alanı: Kullanıcının gelen kutusu veya makale içerikleri gibi sık güncellenen kaynaklar. Ayrıca, avatarlar gibi önemsiz içerikler için de faydalıdır ancak dikkatli olmak gerekir.

Bir istek önbellekte hiçbir şeyle eşleşmezse ağdan alın, sayfaya gönderin ve aynı anda önbelleğe ekleyin.

Bunu avatarlar gibi bir dizi URL için yaparsanız kaynağınızın depolama alanını artırmayacağınıza dikkat etmeniz gerekir. Kullanıcının disk alanını geri alması gerekiyorsa ana aday olmak istemezsiniz. Önbellekte artık ihtiyacınız olmayan öğeleri kaldırdığınızdan emin olun.

self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.open('mysite-dynamic').then(function (cache) {
      return cache.match(event.request).then(function (response) {
        return (
          response ||
          fetch(event.request).then(function (response) {
            cache.put(event.request, response.clone());
            return response;
          })
        );
      });
    }),
  );
});

Bellek kullanımının verimli olmasını sağlamak için bir yanıtın/isteğin gövdesini yalnızca bir kez okuyabilirsiniz. Yukarıdaki kod, ayrı olarak okunabilecek ek kopyalar oluşturmak için .clone() değerini kullanır.

trained-to-thrill kanalında, Flickr resimlerini önbelleğe almak için bu yöntemi kullanıyorum.

Yeniden doğrulama sırasında eski tarihli

Yeniden doğrulama sırasında eski.
Yeniden doğrulama sırasında eski.

Kullanım alanı: En son sürüme sahip olmanın gerekli olmadığı, sık sık güncellenen kaynaklar. Avatarlar bu kategoriye girebilir.

Önbelleğe alınmış bir sürüm varsa bunu kullanın ancak bir sonraki sefer için güncelleme alın.

self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.open('mysite-dynamic').then(function (cache) {
      return cache.match(event.request).then(function (response) {
        var fetchPromise = fetch(event.request).then(function (networkResponse) {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        });
        return response || fetchPromise;
      });
    }),
  );
});

Bu, HTTP'nin stale-while-revalidate özelliğine çok benzer.

Push mesajında

Push mesajı.
Push mesajında

Push API, Service Worker'ın üzerine inşa edilmiş başka bir özelliktir. Bu sayede Hizmet İşleyici, işletim sisteminin mesajlaşma hizmetinden gelen bir mesaja yanıt olarak uyandırılabilir. Bu durum, kullanıcının sitenizde açık bir sekmesi olmasa bile gerçekleşir. Yalnızca hizmet çalışanı uyandırılır. Bir sayfadan bunu yapmak için izin istediğinizde kullanıcıya istem gösterilir.

Kullanım alanı: Bildirimle ilgili içerikler (ör. sohbet mesajı, son dakika haberi veya e-posta). Ayrıca, yapılacaklar listesi güncellemesi veya takvim değişikliği gibi anında senkronizasyondan yararlanan içeriklerin sık sık değiştirilmesi de bu kapsamdadır.

Sık karşılaşılan bir sonuç, dokunulduğunda ilgili bir sayfayı açan/odağı bu sayfaya alan bir bildirimdir. Bu bildirim için önbellekleri bu işlemden önce güncellemek son derece önemlidir. Kullanıcının push mesajını aldığı sırada internete bağlı olduğu açıktır, ancak bildirimle nihai olarak etkileşime girdiği zaman internete bağlı olmayabilir. Bu nedenle, bu içeriğin çevrimdışı olarak kullanılabilmesini sağlamak önemlidir.

Bu kod, bildirim göstermeden önce önbellekleri günceller:

self.addEventListener('push', function (event) {
  if (event.data.text() == 'new-email') {
    event.waitUntil(
      caches
        .open('mysite-dynamic')
        .then(function (cache) {
          return fetch('/inbox.json').then(function (response) {
            cache.put('/inbox.json', response.clone());
            return response.json();
          });
        })
        .then(function (emails) {
          registration.showNotification('New email', {
            body: 'From ' + emails[0].from.name,
            tag: 'new-email',
          });
        }),
    );
  }
});

self.addEventListener('notificationclick', function (event) {
  if (event.notification.tag == 'new-email') {
    // Assume that all of the resources needed to render
    // /inbox/ have previously been cached, e.g. as part
    // of the install handler.
    new WindowClient('/inbox/');
  }
});

Arka plan senkronizasyonu açık

Arka plan senkronizasyonu açık.
Arka plan senkronizasyonunu etkinleştirin.

Arka planda senkronizasyon, Service Worker'ın üzerine inşa edilmiş başka bir özelliktir. Arka plan veri senkronizasyonunu tek seferlik olarak veya (son derece sezgisel) bir aralıkla istemenize olanak tanır. Bu durum, kullanıcının sitenizde açık bir sekmesi olmasa bile gerçekleşir. Yalnızca Hizmet Çalışanı uyandırılır. Bir sayfadan bunu yapmak için izin istediğinizde kullanıcıdan izin istenir.

Kullanım alanları: Acil olmayan güncellemeler (özellikle de sosyal medya zaman çizelgeleri veya haber makaleleri gibi güncelleme başına bir push mesajının kullanıcılar için çok sık olacağı kadar düzenli olarak gerçekleşenler).

self.addEventListener('sync', function (event) {
  if (event.id == 'update-leaderboard') {
    event.waitUntil(
      caches.open('mygame-dynamic').then(function (cache) {
        return cache.add('/leaderboard.json');
      }),
    );
  }
});

Önbellek kalıcılığı

Kaynağınıza, istediği gibi kullanabileceği belirli bir miktarda boş alan verilir. Bu boş alan, tüm kaynak depolama alanları arasında paylaşılır: (yerel) Depolama, IndexedDB, Dosya Sistemi Erişimi ve tabii ki Önbellekler.

Aldığınız tutar belirtilmez. Bu süre, cihaza ve depolama koşullarına bağlı olarak değişir. Ne kadar krediniz olduğunu aşağıdaki yöntemlerle öğrenebilirsiniz:

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}

Ancak tüm tarayıcı depolama alanlarında olduğu gibi, cihaz depolama alanı sıkıntısı yaşarsa tarayıcının verilerinizi silmesine izin verilir. Maalesef tarayıcı, ne pahasına olursa olsun saklamak istediğiniz filmler ile çok önemsemediğiniz oyunlar arasında ayrım yapamaz.

Geçici bir çözüm için StorageManager arayüzünü kullanın:

// From a page:
navigator.storage.persist()
.then(function(persisted) {
  if (persisted) {
    // Hurrah, your data is here to stay!
  } else {
   // So sad, your data may get chucked. Sorry.
});

Elbette kullanıcının izin vermesi gerekir. Bunun için Permissions API'yi kullanın.

Artık silme işlemi kontrolünün kullanıcıda olmasını bekleyebileceğimizden, kullanıcıyı bu akışın bir parçası haline getirmek önemlidir. Cihazın depolama alanı dolduğunda ve gerekli olmayan verileri temizlemek sorunu çözmüyorsa kullanıcı hangi öğeleri saklayacağını ve hangilerini kaldıracağını kendisi belirler.

Bunun işe yaraması için işletim sistemlerinin, tarayıcıyı tek bir öğe olarak raporlamak yerine depolama alanı dökümlerinde "dayanıklı" kaynaklarını platforma özel uygulamalara eşdeğer olarak ele alması gerekir.

Yayınlama Önerileri: İsteklere yanıt verme

Ne kadar önbelleğe alma işlemi yaparsanız yapın, hizmet çalışanı ne zaman ve nasıl kullanılacağını söylemediğiniz sürece önbelleği kullanmaz. İstekleri işleme konusunda birkaç kalıp aşağıda verilmiştir:

Yalnızca önbellek

Yalnızca önbelleğe alma.
Yalnızca önbellek.

Kullanım alanı: Sitenizin belirli bir "sürümünde" statik olarak kabul ettiğiniz her şey. Bunları yükleme etkinliğinde önbelleğe almanız gerekir. Böylece, bu bilgilerin orada bulunduğundan emin olabilirsiniz.

self.addEventListener('fetch', function (event) {
  // If a match isn't found in the cache, the response
  // will look like a connection error
  event.respondWith(caches.match(event.request));
});

Genellikle bu durumu özel olarak ele almanız gerekmez. Ağ üzerinden yedekleme için önbelleğe alma bu durumu kapsar.

Yalnızca ağ

Yalnızca ağ.
Yalnızca ağ.

Kullanım alanı: Analytics ping'leri, GET olmayan istekler gibi çevrimdışı eşdeğeri olmayan öğeler.

self.addEventListener('fetch', function (event) {
  event.respondWith(fetch(event.request));
  // or simply don't call event.respondWith, which
  // will result in default browser behavior
});

Genellikle bu durumu özel olarak ele almanız gerekmez. Ağ üzerinden yedekleme için önbelleğe alma bu durumu kapsar.

Önbellek, ağa geri dönüyor

Önbelleğe alma, ağa geri dönme.
Önbelleğe alma, ağ bağlantısına geri dönme.

Kullanım alanı: Çevrimdışı öncelikli modeller oluşturma. Bu gibi durumlarda, isteklerin çoğunu bu şekilde yönetirsiniz. Diğer kalıplar, gelen isteğe bağlı istisnalardır.

self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.match(event.request).then(function (response) {
      return response || fetch(event.request);
    }),
  );
});

Bu, önbellekteki öğeler için "yalnızca önbellek" davranışını ve önbelleğe alınmamış her şey için (önbelleğe alınamadığı için GET olmayan tüm istekleri içerir) "yalnızca önbellek" davranışını sağlar.

Önbellek ve ağ yarışı

Önbellek ve ağ yarışı.
Önbellek ve ağ yarışı.

Kullanım alanı: Yavaş disk erişimi olan cihazlarda performansı artırmaya çalıştığınız küçük öğeler.

Eski sabit diskler, virüs tarayıcıları ve daha hızlı internet bağlantıları gibi bazı kombinasyonlar göz önünde bulundurulduğunda, kaynakları ağdan almak diske geçmekten daha hızlı olabilir. Ancak, kullanıcının cihazında içerik varken ağa gitmek veri israfı olabilir. Bu nedenle, bunu göz önünde bulundurun.

// Promise.race is no good to us because it rejects if
// a promise rejects before fulfilling. Let's make a proper
// race function:
function promiseAny(promises) {
  return new Promise((resolve, reject) => {
    // make sure promises are all promises
    promises = promises.map((p) => Promise.resolve(p));
    // resolve this promise as soon as one resolves
    promises.forEach((p) => p.then(resolve));
    // reject if all promises reject
    promises.reduce((a, b) => a.catch(() => b)).catch(() => reject(Error('All failed')));
  });
}

self.addEventListener('fetch', function (event) {
  event.respondWith(promiseAny([caches.match(event.request), fetch(event.request)]));
});

Ağ, önbelleğe geri dönüyor

Ağ önbelleğe alınıyor.
Ağ, önbelleğe geri dönüyor.

İdeal kullanım alanı: Sitenin "sürümünün" dışında sık sık güncellenen kaynaklar için hızlı düzeltme. Örneğin, makaleler, avatarlar, sosyal medya zaman çizelgeleri ve oyun skor tabloları.

Bu sayede, online kullanıcılara en güncel içeriği sunarken çevrimdışı kullanıcılara daha eski bir önbelleğe alınmış sürüm sunarsınız. Ağ isteği başarılı olursa büyük olasılıkla önbelleğe alma girişini güncellemek istersiniz.

Ancak bu yöntemin kusurları vardır. Kullanıcının kesintili veya yavaş bağlantısı varsa zaten cihazında mükemmel şekilde kabul edilebilir içeriği elde etmeden önce ağın başarısız olmasını beklemesi gerekir. Bu işlem çok uzun sürebilir ve kullanıcılar için can sıkıcı bir deneyimdir. Daha iyi bir çözüm için bir sonraki kalıbı (Önbellek sonra ağ) inceleyin.

self.addEventListener('fetch', function (event) {
  event.respondWith(
    fetch(event.request).catch(function () {
      return caches.match(event.request);
    }),
  );
});

Önbelleğe alma ve ardından ağ

Önbellek ve ardından ağ.
Önce önbelleğe alın, ardından ağa aktarın.

Kullanım alanı: Sık güncellenen içerikler. Örneğin, makaleler, sosyal medya zaman çizelgeleri ve oyun skor tabloları.

Bu, sayfanın biri önbelleğe, diğeri ağa olmak üzere iki istek yapmasını gerektirir. Amaç, önce önbelleğe alınmış verileri göstermek, ardından ağ verileri geldiğinde/gelirse sayfayı güncellemektir.

Bazen yeni veriler geldiğinde mevcut verileri değiştirebilirsiniz (ör. oyun skor tablosu) ancak bu, daha büyük içerikler nedeniyle rahatsız edici olabilir. Kullanıcının okuyabileceği veya etkileşimde bulunabileceği bir öğeyi "kaybetmeyin".

Twitter, yeni içeriği eski içeriğin üzerine ekler ve kullanıcının deneyimi kesintiye uğramayacak şekilde kaydırma konumunu ayarlar. Bu, Twitter'ın içerikleri çoğunlukla doğrusal bir düzende tutmasından kaynaklanır. Heyecan verici için bu kalıbı kopyaladım. Böylece, içeriği olabildiğince hızlı bir şekilde ekrana getirirken güncel içeriği de gelir gelmez gösterebilirim.

Sayfadaki kod:

var networkDataReceived = false;

startSpinner();

// fetch fresh data
var networkUpdate = fetch('/data.json')
  .then(function (response) {
    return response.json();
  })
  .then(function (data) {
    networkDataReceived = true;
    updatePage(data);
  });

// fetch cached data
caches
  .match('/data.json')
  .then(function (response) {
    if (!response) throw Error('No data');
    return response.json();
  })
  .then(function (data) {
    // don't overwrite newer network data
    if (!networkDataReceived) {
      updatePage(data);
    }
  })
  .catch(function () {
    // we didn't get cached data, the network is our last hope:
    return networkUpdate;
  })
  .catch(showErrorMessage)
  .then(stopSpinner);

Hizmet Çalışanı'ndaki kod:

Her zaman ağa gidip bir önbelleği güncellemelisiniz.

self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.open('mysite-dynamic').then(function (cache) {
      return fetch(event.request).then(function (response) {
        cache.put(event.request, response.clone());
        return response;
      });
    }),
  );
});

trained-to-thrill adlı makalede, fetch yerine XHR kullanarak ve Hizmet Çalışanı'na sonucu nereden alacağını (sayfa kodu, Hizmet Çalışanı kodu) bildirmek için Accept üst bilgisini kötüye kullanarak bu sorunu çözdüm.

Genel yedek

Genel yedek.
Genel yedek.

Önbellekten ve/veya ağdan bir şey yayınlayamazsanız genel bir yedek sunmak isteyebilirsiniz.

Kullanım alanları: Avatarlar, başarısız POST istekleri ve "Çevrimdışıyken kullanılamaz" sayfası gibi ikincil görüntüler.

self.addEventListener('fetch', function (event) {
  event.respondWith(
    // Try the cache
    caches
      .match(event.request)
      .then(function (response) {
        // Fall back to network
        return response || fetch(event.request);
      })
      .catch(function () {
        // If both fail, show a generic fallback:
        return caches.match('/offline.html');
        // However, in reality you'd have many different
        // fallbacks, depending on URL and headers.
        // Eg, a fallback silhouette image for avatars.
      }),
  );
});

Yedek olarak kullandığınız öğe muhtemelen bir yükleme bağımlılığı olacaktır.

Sayfanız bir e-posta gönderiyorsa servis çalışanınız, e-postayı IndexedDB "giden kutusu"nda depolamaya geri dönebilir ve gönderimin başarısız olduğunu ancak verilerin başarıyla korunduğunu sayfaya bildirerek yanıt verebilir.

Hizmet çalışanı tarafında şablon oluşturma

ServiceWorker tarafında şablon oluşturma.
ServiceWorker tarafında şablon oluşturma.

Kullanım alanı: Sunucu yanıtlarının önbelleğe alınamadığı sayfalar.

Sayfaları sunucuda oluşturmak işlemleri hızlandırır ancak bu, önbelleğe anlamlı olmayabilecek durum verileri (ör. "… olarak giriş yaptı") dahil etmek anlamına gelebilir. Sayfanız bir hizmet çalışanı tarafından kontrol ediliyorsa bunun yerine bir şablonla birlikte JSON verileri istemeyi ve bunun yerine bu verileri oluşturmayı tercih edebilirsiniz.

importScripts('templating-engine.js');

self.addEventListener('fetch', function (event) {
  var requestURL = new URL(event.request.url);

  event.respondWith(
    Promise.all([
      caches.match('/article-template.html').then(function (response) {
        return response.text();
      }),
      caches.match(requestURL.path + '.json').then(function (response) {
        return response.json();
      }),
    ]).then(function (responses) {
      var template = responses[0];
      var data = responses[1];

      return new Response(renderTemplate(template, data), {
        headers: {
          'Content-Type': 'text/html',
        },
      });
    }),
  );
});

Tümünü bir araya getirme

Bu yöntemlerden biriyle sınırlı değilsiniz. Hatta istek URL'sine bağlı olarak bunların çoğunu kullanmanız muhtemeldir. Örneğin, heyecan verici özelliğinde şunlar kullanılır:

İsteği inceleyip ne yapmanız gerektiğine karar vermeniz yeterlidir:

self.addEventListener('fetch', function (event) {
  // Parse the URL:
  var requestURL = new URL(event.request.url);

  // Handle requests to a particular host specifically
  if (requestURL.hostname == 'api.example.com') {
    event.respondWith(/* some combination of patterns */);
    return;
  }
  // Routing for local URLs
  if (requestURL.origin == location.origin) {
    // Handle article URLs
    if (/^\/article\//.test(requestURL.pathname)) {
      event.respondWith(/* some other combination of patterns */);
      return;
    }
    if (/\.webp$/.test(requestURL.pathname)) {
      event.respondWith(/* some other combination of patterns */);
      return;
    }
    if (request.method == 'POST') {
      event.respondWith(/* some other combination of patterns */);
      return;
    }
    if (/cheese/.test(requestURL.pathname)) {
      event.respondWith(
        new Response('Flagrant cheese error', {
          status: 512,
        }),
      );
      return;
    }
  }

  // A sensible default pattern
  event.respondWith(
    caches.match(event.request).then(function (response) {
      return response || fetch(event.request);
    }),
  );
});

…anladınız.

Kredi

…sevimli simgeler için:

  • buzzyrobot tarafından Code
  • Scott Lewis tarafından Takvim
  • Ağı: Ben Rizzo
  • SD - Thomas Le Bas
  • iconsmind.com tarafından oluşturulan CPU
  • trasnik tarafından Çöp Kutusu
  • @daosme tarafından gönderilen bildirim
  • Mister Pixel tarafından Layout
  • Cloud - P.J. Onori

"Yayınla"ya basmadan önce bazı uğultu hatalarını yakaladığın için Jeff Posnick'e de teşekkür ederim.

Daha fazla bilgi