Strategi untuk caching pekerja layanan

Sampai sekarang, baru ada penyebutan dan cuplikan kode kecil dari Antarmuka Cache. Untuk menggunakan pekerja layanan secara efektif, Anda perlu mengadopsi satu atau lebih strategi {i>caching<i}, yang memerlukan sedikit pemahaman tentang antarmuka Cache.

Strategi caching adalah interaksi antara peristiwa fetch pekerja layanan dan antarmuka Cache. Cara penulisan strategi penyimpanan dalam cache bergantung; misalnya, mungkin lebih baik menangani permintaan untuk aset statis daripada dokumen, dan hal ini memengaruhi cara membuat strategi penyimpanan dalam cache.

Sebelum kita membahas strategi, mari kita luangkan waktu sejenak untuk membahas apa yang tidak ada di antarmuka Cache, dan ikhtisar singkat dari beberapa metode yang ditawarkannya untuk mengelola cache pekerja layanan.

Antarmuka Cache versus cache HTTP

Jika Anda belum pernah menggunakan antarmuka Cache, hal itu mungkin tergoda untuk menganggapnya sama dengan, atau setidaknya terkait dengan {i>cache<i} HTTP. Bukan itu masalahnya.

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

Hal ini membantu untuk menganggap {i> cache browser<i} sebagai berlapis. Cache HTTP adalah cache tingkat rendah yang didorong oleh key-value pair 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 bila menggunakan pasangan nilai kunci HTTP yang relatif sederhana, dan merupakan setengah dari sekian banyak yang memungkinkan strategi {i>caching<i}. Beberapa metode API penting seputar cache pekerja layanan adalah:

Ini hanya beberapa di antaranya. Ada metode lain yang berguna, yang akan digunakan nanti dalam panduan ini.

Acara fetch sederhana

Setengah lainnya dari strategi caching adalah Peristiwa fetch. Sejauh ini dalam dokumentasi ini, Anda telah mendengar sedikit tentang "mencegatan permintaan jaringan", dan peristiwa fetch di dalam pekerja layanan adalah tempat hal ini terjadi:

// 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 itu adalah salah satu yang memberikan gambaran sekilas tentang apa yang dapat dilakukan pekerja layanan. Kode di atas melakukan hal berikut:

  1. Periksa properti destination permintaan untuk melihat apakah ini adalah permintaan gambar.
  2. Jika gambar berada di cache pekerja layanan, tayangkan dari sana. Jika tidak, ambil gambar dari jaringan, menyimpan respons dalam {i>cache<i}, dan mengembalikan respons jaringan.
  3. Semua permintaan lain diteruskan melalui pekerja layanan tanpa interaksi dengan cache.

Objek event pengambilan berisi Properti request informasi yang berguna untuk membantu Anda mengidentifikasi tipe setiap permintaan:

  • url, yaitu 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 digunakan untuk membedakan permintaan dokumen HTML dengan permintaan lainnya.
  • destination, yang mendeskripsikan jenis konten yang diminta dengan cara yang menghindari penggunaan ekstensi file aset yang diminta.

Sekali lagi, asynchrony adalah nama game. Anda akan ingat bahwa peristiwa install menawarkan event.waitUntil yang menggunakan promise, dan menunggunya diselesaikan sebelum melanjutkan ke aktivasi. Peristiwa fetch menawarkan Metode event.respondWith yang dapat digunakan untuk menampilkan hasil permintaan fetch atau respons yang ditampilkan oleh antarmuka Cache Metode match.

Strategi penyimpanan dalam cache

Setelah Anda sedikit memahami instance Cache dan pengendali peristiwa fetch, Anda siap untuk mempelajari lebih dalam beberapa strategi {i>caching<i} pekerja layanan. Sementara kemungkinannya tidak ada batasnya, panduan ini akan tetap mengacu pada strategi yang disertakan dengan Workbox, sehingga Anda bisa mengetahui apa yang terjadi di internal {i>Workbox<i}.

Cache saja

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

Mari kita mulai dengan strategi penyimpanan cache sederhana yang akan kita sebut "Hanya Cache". Hanya saja: ketika pekerja layanan mengontrol halaman, permintaan yang sesuai hanya akan masuk ke cache. Artinya, setiap aset yang di-cache harus di-pra-cache agar tersedia agar pola dapat berfungsi, dan aset tersebut tidak akan diperbarui di cache hingga 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-pra-cache pada waktu penginstalan. Ketika pekerja layanan menangani pengambilan, kami akan memeriksa apakah URL permintaan yang ditangani oleh peristiwa fetch ada dalam array aset yang telah disimpan sebelumnya. Jika demikian, kita mengambil resource dari cache, dan melewati jaringan. Permintaan lain diteruskan ke jaringan, dan hanya jaringan. Untuk melihat cara kerja strategi ini, lihat demo ini dengan konsol Anda dalam keadaan terbuka.

Khusus jaringan

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

Kebalikan dari "Cache Only" adalah "Hanya Jaringan", di mana permintaan diteruskan melalui pekerja layanan ke jaringan tanpa interaksi apa pun dengan cache pekerja layanan. Ini adalah strategi yang baik untuk memastikan keaktualan konten (seperti markup), tetapi konsekuensinya adalah, ia tidak akan pernah berfungsi ketika pengguna sedang {i>offline<i}.

Memastikan permintaan melewati jaringan berarti Anda tidak memanggil event.respondWith untuk permintaan yang cocok. Jika Anda ingin bersikap eksplisit, Anda dapat menampar return; kosong di callback peristiwa fetch untuk permintaan yang ingin diteruskan ke jaringan. Inilah yang terjadi di dalam kotak "Cache Only" demo strategi untuk permintaan yang tidak di-pra-cache.

Cache terlebih dahulu, beralih kembali ke jaringan

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

Strategi ini adalah momen di mana segala sesuatunya menjadi lebih terlibat. Untuk permintaan pencocokan, prosesnya berjalan seperti ini:

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

Berikut adalah contoh strategi ini, yang dapat Anda uji 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 yang memiliki versi {i>hash<i}. Layanan ini menawarkan peningkatan kecepatan untuk aset yang tidak dapat diubah dengan meniadakan pemeriksaan keaktualan konten dengan server yang mungkin dimulai oleh cache HTTP. Yang lebih penting, aset yang di-cache akan tersedia secara offline.

Jaringan terlebih dahulu, beralih kembali ke cache

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

Jika Anda membalik "Cache pertama, jaringan kedua" di kepalanya, Anda akan mendapati pesan "Jaringan terlebih dahulu, cache kedua" strategi, seperti namanya:

  1. Anda masuk ke jaringan terlebih dahulu untuk membuat permintaan, dan menempatkan respons di cache.
  2. Jika Anda kemudian offline, Anda kembali ke versi terbaru dari respons itu dalam {i>cache<i}.

Strategi ini sangat bagus untuk permintaan HTML atau API saat, saat sedang {i>online<i}, Anda ingin versi terbaru dari sumber daya, namun ingin memberikan akses offline ke versi terbaru yang tersedia. Berikut adalah tampilan yang mungkin akan terlihat bila 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 tanggapan HTML ditempatkan dalam cache. Kemudian di {i>developer tools<i} Anda, menyimulasikan koneksi offline, lalu muat ulang lagi. Versi terakhir yang tersedia akan langsung disajikan dari cache.

Dalam situasi di mana kemampuan offline itu penting, tetapi Anda perlu menyeimbangkan kemampuan itu dengan akses ke versi terbaru dari sedikit markup atau data API, "Jaringan terlebih dahulu, cache kedua" merupakan strategi mantap yang mencapai sasaran tersebut.

Tidak berlaku saat validasi ulang

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

Strategi yang telah kita bahas sejauh ini, "Stale-temporary-validate" yang paling kompleks. Hal ini mirip dengan dua strategi terakhir dalam beberapa hal, tetapi prosedurnya memprioritaskan kecepatan akses untuk sumber daya, sambil tetap memperbaruinya di latar belakang. Strategi ini berjalan seperti:

  1. Pada permintaan pertama untuk aset, ambillah dari jaringan, memasukkannya ke dalam {i>cache<i}, dan mengembalikan respons jaringan.
  2. Pada permintaan berikutnya, tayangkan aset dari cache terlebih dahulu, lalu "di latar belakang", memintanya lagi dari jaringan dan memperbarui entri cache aset.
  3. Untuk permintaan setelahnya, Anda akan menerima versi terakhir yang diambil dari jaringan yang ditempatkan di {i>cache<i} pada langkah sebelumnya.

Ini adalah strategi yang sangat bagus untuk hal-hal yang sema penting untuk selalu diperbarui, tetapi tidak penting. Anggap saja hal-hal seperti avatar untuk situs media sosial. Mereka mendapatkan pembaruan ketika pengguna melakukannya, tetapi versi terbaru tidak sepenuhnya 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 cara kerjanya di sebuah demo langsung lainnya, terutama jika Anda memperhatikan tab jaringan di alat pengembang browser Anda, dan penampil CacheStorage (jika alat developer browser Anda memiliki alat tersebut).

Lanjutkan ke Workbox!

Dokumen ini merangkum ulasan kita tentang API pekerja layanan, serta API terkait, yang berarti Anda sudah cukup belajar tentang cara menggunakan pekerja layanan secara langsung untuk mulai mengutak-atik Workbox!