Strategi untuk caching pekerja layanan

Hingga saat ini, hanya ada sebutan dan cuplikan kode kecil dari antarmuka Cache. Untuk menggunakan pekerja layanan secara efektif, Anda perlu mengadopsi satu atau beberapa strategi caching, yang memerlukan sedikit pemahaman tentang antarmuka Cache.

Strategi caching adalah interaksi antara peristiwa fetch pekerja layanan dan antarmuka Cache. Cara penulisan strategi caching bergantung; misalnya, mungkin lebih baik menangani permintaan untuk aset statis secara berbeda dari dokumen, dan hal ini memengaruhi cara penyusunan strategi caching.

Sebelum kita membahas strategi itu sendiri, mari kita luangkan waktu untuk membahas apa yang bukan merupakan antarmuka Cache, pengertiannya, dan ikhtisar singkat beberapa metode yang ditawarkan untuk mengelola cache pekerja layanan.

Antarmuka Cache versus cache HTTP

Jika Anda belum pernah menangani antarmuka Cache sebelumnya, Anda mungkin ingin menganggapnya sama dengan, atau setidaknya terkait dengan cache HTTP. Bukan itu masalahnya.

  • Antarmuka Cache adalah mekanisme cache yang sepenuhnya terpisah dari cache HTTP.
  • Konfigurasi Cache-Control apa pun yang Anda gunakan untuk memengaruhi cache HTTP tidak memengaruhi aset yang disimpan di antarmuka Cache.

Ada baiknya untuk menganggap cache browser sebagai berlapis. Cache HTTP adalah cache tingkat rendah yang didorong oleh pasangan nilai-kunci dengan perintah yang dinyatakan dalam header HTTP.

Sebaliknya, antarmuka Cache adalah cache tingkat tinggi yang didorong oleh JavaScript API. Cara ini menawarkan lebih banyak fleksibilitas dibandingkan saat menggunakan key-value pair HTTP yang relatif sederhana, dan merupakan setengah dari jumlah yang memungkinkan strategi caching. Beberapa metode API penting seputar cache pekerja layanan adalah:

Ini hanya beberapa di antaranya. Ada metode berguna lainnya, tetapi ini adalah metode dasar yang akan Anda lihat digunakan nanti dalam panduan ini.

Acara fetch yang simpel

Bagian lainnya dari strategi caching adalah peristiwa fetch pekerja layanan. Sejauh ini dalam dokumentasi ini, Anda telah mendengar sedikit tentang "intersepsi permintaan jaringan", dan peristiwa fetch di dalam pekerja layanan adalah tempat terjadinya:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('install', (event) => {
  event.waitUntil(caches.open(cacheName));
});

self.addEventListener('fetch', async (event) => {
  // Is this a request for an image?
  if (event.request.destination === 'image') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Respond with the image from the cache or from the network
      return cache.match(event.request).then((cachedResponse) => {
        return cachedResponse || fetch(event.request.url).then((fetchedResponse) => {
          // Add the network response to the cache for future visits.
          // Note: we need to make a copy of the response to save it in
          // the cache and use the original as the request response.
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

Ini adalah contoh mainan—dan yang dapat Anda lihat sendiri—tetapi ini adalah contoh yang memberikan gambaran sekilas tentang apa yang dapat dilakukan pekerja layanan. Kode di atas melakukan hal berikut:

  1. Periksa properti destination permintaan untuk mengetahui apakah ini adalah permintaan gambar.
  2. Jika gambar ada di cache pekerja layanan, tayangkan dari sana. Jika tidak, ambil gambar dari jaringan, simpan respons dalam cache, dan tampilkan respons jaringan.
  3. Semua permintaan lainnya diteruskan melalui pekerja layanan tanpa interaksi dengan cache.

Objek event pengambilan berisi properti request yang berisi beberapa bit informasi berguna untuk membantu Anda mengidentifikasi jenis setiap permintaan:

  • url, yang merupakan URL untuk permintaan jaringan yang saat ini ditangani oleh peristiwa fetch.
  • method, yang merupakan metode permintaan (mis., GET atau POST).
  • mode, yang menjelaskan mode permintaan. Nilai 'navigate' sering kali digunakan untuk membedakan permintaan dokumen HTML dengan permintaan lainnya.
  • destination, yang menjelaskan jenis konten yang diminta dengan cara yang menghindari penggunaan ekstensi file aset yang diminta.

Sekali lagi, nama gamenya asinkron. Anda akan ingat bahwa peristiwa install menawarkan metode event.waitUntil yang mengambil promise dan menunggu hingga selesai sebelum melanjutkan ke aktivasi. Peristiwa fetch menawarkan metode event.respondWith serupa yang dapat Anda gunakan untuk menampilkan hasil permintaan fetch asinkron atau respons yang ditampilkan oleh metode match antarmuka Cache.

Strategi penyimpanan ke cache

Setelah sedikit memahami instance Cache dan pengendali peristiwa fetch, Anda siap mempelajari beberapa strategi caching pekerja layanan. Meskipun kemungkinannya tidak terbatas, panduan ini akan tetap menggunakan strategi yang disertakan dengan Workbox, sehingga Anda dapat mengetahui apa yang terjadi di bagian dalam Workbox.

Hanya cache

Menampilkan alur dari halaman, ke pekerja layanan, ke cache.

Mari kita mulai dengan strategi {i>caching<i} sederhana yang kita sebut "Hanya Cache". Hanya saja: ketika pekerja layanan mengontrol halaman, permintaan yang cocok hanya akan masuk ke cache. Artinya, setiap aset yang di-cache harus di-precache agar tersedia agar pola dapat berfungsi, dan aset tersebut tidak akan pernah diupdate dalam cache sampai pekerja layanan diperbarui.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

// Assets to precache
const precachedAssets = [
  '/possum1.jpg',
  '/possum2.jpg',
  '/possum3.jpg',
  '/possum4.jpg'
];

self.addEventListener('install', (event) => {
  // Precache assets on install
  event.waitUntil(caches.open(cacheName).then((cache) => {
    return cache.addAll(precachedAssets);
  }));
});

self.addEventListener('fetch', (event) => {
  // Is this one of our precached assets?
  const url = new URL(event.request.url);
  const isPrecachedRequest = precachedAssets.includes(url.pathname);

  if (isPrecachedRequest) {
    // Grab the precached asset from the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request.url);
    }));
  } else {
    // Go to the network
    return;
  }
});

Di atas, array aset di-precache saat penginstalan. Saat pekerja layanan menangani pengambilan, kita akan memeriksa apakah URL permintaan yang ditangani oleh peristiwa fetch berada dalam array aset yang di-cache. Jika demikian, kita mengambil sumber daya dari {i>cache<i}, dan melewati jaringan. Permintaan lain melewati jaringan, dan hanya jaringan. Untuk melihat penerapan strategi ini, lihat demo ini dengan konsol Anda terbuka.

Khusus jaringan

Menampilkan alur dari halaman, ke pekerja layanan, ke jaringan.

Kebalikan dari "Cache Only" adalah "Hanya Jaringan", permintaan diteruskan melalui pekerja layanan ke jaringan tanpa interaksi apa pun dengan cache pekerja layanan. Ini adalah strategi yang bagus untuk memastikan keaktualan konten (misalnya markup), tetapi konsekuensinya adalah bahwa konten tidak akan pernah berfungsi saat pengguna offline.

Memastikan permintaan melewati jaringan berarti Anda tidak memanggil event.respondWith untuk permintaan yang sesuai. Jika ingin lebih eksplisit, Anda dapat menerapkan return; kosong dalam callback peristiwa fetch untuk permintaan yang ingin diteruskan ke jaringan. Inilah yang terjadi dalam demo strategi "Hanya Cache" untuk permintaan yang tidak di-pracache.

{i>Cache<i} terlebih dahulu, kembali ke jaringan

Menampilkan alur dari halaman, ke pekerja layanan, ke cache, lalu ke jaringan jika tidak ada dalam cache.

Strategi ini adalah saat segala sesuatu menjadi lebih terlibat. Untuk permintaan yang cocok, prosesnya berjalan seperti ini:

  1. Permintaan mencapai cache. Jika aset ada di cache, tayangkan dari sana.
  2. Jika permintaan tidak ada di dalam cache, buka jaringan.
  3. Setelah permintaan jaringan selesai, tambahkan ke cache, lalu tampilkan respons dari jaringan.

Berikut adalah contoh strategi ini, yang dapat Anda uji dalam demo langsung:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a request for an image
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the cache first
      return cache.match(event.request.url).then((cachedResponse) => {
        // Return a cached response if we have one
        if (cachedResponse) {
          return cachedResponse;
        }

        // Otherwise, hit the network
        return fetch(event.request).then((fetchedResponse) => {
          // Add the network response to the cache for later visits
          cache.put(event.request, fetchedResponse.clone());

          // Return the network response
          return fetchedResponse;
        });
      });
    }));
  } else {
    return;
  }
});

Meskipun contoh ini hanya mencakup gambar, ini adalah strategi yang bagus untuk diterapkan pada semua aset statis (seperti CSS, JavaScript, gambar, dan font), terutama aset berversi hash. Layanan ini menawarkan peningkatan kecepatan untuk aset yang tidak dapat diubah dengan melakukan pemeriksaan keaktualan konten pada server yang mungkin akan dimulai oleh cache HTTP. Yang lebih penting, setiap aset yang disimpan dalam cache akan tersedia secara offline.

Jaringan pertama, fallback ke cache

Menampilkan alur dari halaman, ke pekerja layanan, ke jaringan, lalu ke cache jika jaringan tidak tersedia.

Jika Anda menggeser "Cache pertama, jaringan kedua" di kepalanya, Anda akan mendapatkan strategi "Jaringan pertama, cache kedua", yang terdengar seperti ini:

  1. Anda masuk ke jaringan terlebih dahulu untuk meminta permintaan, lalu menempatkan respons di dalam cache.
  2. Jika Anda offline di kemudian hari, Anda kembali ke versi terbaru dari respons tersebut dalam cache.

Strategi ini sangat bagus untuk permintaan HTML atau API ketika, selagi online, Anda menginginkan versi resource terbaru, tetapi ingin memberikan akses offline ke versi terbaru yang tersedia. Berikut adalah tampilannya jika diterapkan ke permintaan untuk HTML:

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  // Check if this is a navigation request
  if (event.request.mode === 'navigate') {
    // Open the cache
    event.respondWith(caches.open(cacheName).then((cache) => {
      // Go to the network first
      return fetch(event.request.url).then((fetchedResponse) => {
        cache.put(event.request, fetchedResponse.clone());

        return fetchedResponse;
      }).catch(() => {
        // If the network is unavailable, get
        return cache.match(event.request.url);
      });
    }));
  } else {
    return;
  }
});

Anda dapat mencobanya dalam demo. Pertama, buka halaman. Anda mungkin perlu memuat ulang sebelum respons HTML ditempatkan dalam cache. Lalu, di alat developer Anda, simulasikan koneksi offline, dan muat ulang lagi. Versi terakhir yang tersedia akan langsung disalurkan dari cache.

Dalam situasi ketika kemampuan offline dianggap penting, tetapi Anda perlu menyeimbangkan kemampuan tersebut dengan akses ke sedikit data markup atau API versi terbaru, "Jaringan pertama, cache kedua" adalah strategi solid yang mencapai sasaran tersebut.

Tidak berlaku saat validasi ulang

Menampilkan alur dari halaman, ke pekerja layanan, ke cache, lalu dari jaringan ke cache.

Dari strategi yang telah kita bahas sejauh ini, "Basih saat-validasi ulang" adalah yang paling kompleks. Ini mirip dengan dua strategi terakhir dalam beberapa hal, tetapi prosedurnya memprioritaskan kecepatan akses untuk resource, sekaligus terus memperbaruinya di latar belakang. Strategi ini berjalan seperti:

  1. Pada permintaan pertama untuk aset, ambil dari jaringan, tempatkan dalam cache, dan tampilkan respons jaringan.
  2. Pada permintaan berikutnya, tayangkan aset dari cache terlebih dahulu, lalu "di latar belakang", minta kembali aset dari jaringan dan perbarui entri cache aset.
  3. Untuk permintaan setelah itu, Anda akan menerima versi terakhir yang diambil dari jaringan yang ditempatkan di cache pada langkah sebelumnya.

Ini adalah strategi yang sangat baik untuk hal-hal yang semacam penting untuk diperbarui, tetapi tidak krusial. Pikirkan hal-hal seperti avatar untuk situs media sosial. Library ini diupdate saat pengguna biasa melakukannya, tetapi versi terbaru tidak benar-benar diperlukan pada setiap permintaan.

// Establish a cache name
const cacheName = 'MyFancyCacheName_v1';

self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image') {
    event.respondWith(caches.open(cacheName).then((cache) => {
      return cache.match(event.request).then((cachedResponse) => {
        const fetchedResponse = fetch(event.request).then((networkResponse) => {
          cache.put(event.request, networkResponse.clone());

          return networkResponse;
        });

        return cachedResponse || fetchedResponse;
      });
    }));
  } else {
    return;
  }
});

Anda dapat melihat penerapannya di demonstrasi langsung lainnya, terutama jika Anda memperhatikan tab jaringan pada alat developer browser dan penampil CacheStorage-nya (jika alat developer browser Anda memiliki alat tersebut).

Maju ke Workbox!

Dokumen ini mengakhiri peninjauan kami tentang API pekerja layanan, serta API terkait, yang berarti Anda telah cukup mempelajari cara menggunakan pekerja layanan secara langsung untuk mulai mengutak-atik Workbox.