Panduan cache imperatif

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

Beberapa situs mungkin perlu berkomunikasi dengan pekerja layanan tanpa perlu mengetahui hasilnya. Berikut beberapa contohnya:

  • Halaman mengirim pekerja layanan daftar URL untuk mengambil data sehingga, saat pengguna mengklik link, dokumen atau subresource halaman sudah tersedia di cache, sehingga navigasi selanjutnya jauh lebih cepat.
  • Halaman akan meminta pekerja layanan untuk mengambil dan meng-cache kumpulan artikel teratas agar tersedia untuk tujuan offline.

Pendelegasian jenis tugas non-kritis ini ke pekerja layanan akan berguna, yaitu mengosongkan thread utama, untuk menangani tugas yang lebih mendesak dengan lebih baik, seperti merespons interaksi pengguna.

Diagram halaman yang meminta resource untuk meng-cache ke service worker.

Dalam panduan ini, kita akan mempelajari cara menerapkan teknik komunikasi satu arah dari halaman ke pekerja layanan menggunakan API browser standar dan library Workbox. Kita akan menyebut jenis kasus penggunaan ini sebagai cache imperatif.

Kasus produksi

1-800-Flowers.com menerapkan cache imperatif (pengambilan data) dengan pekerja layanan melalui postMessage() untuk mengambil data item teratas di halaman kategori guna mempercepat navigasi berikutnya ke halaman detail produk.

Logo 1-800 Flowers.

Mereka menggunakan pendekatan campuran untuk memutuskan item mana yang akan diambil data:

  • Pada waktu pemuatan halaman, mereka meminta pekerja layanan untuk mengambil data JSON untuk 9 item teratas, dan menambahkan objek respons yang dihasilkan ke cache.
  • Untuk item yang tersisa, item akan memproses peristiwa mouseover , sehingga saat pengguna memindahkan kursor ke atas item, mereka dapat memicu pengambilan resource saat "permintaan".

Contoh tersebut menggunakan Cache API untuk menyimpan respons JSON:

Logo 1-800 Flowers.
Pengambilan data produk JSON lebih dulu dari halaman listingan produk di 1-800Flowers.com.

Ketika pengguna mengklik item, data JSON yang terkait dengan item tersebut dapat diambil dari cache, tanpa perlu masuk ke jaringan, sehingga navigasi menjadi lebih cepat.

Menggunakan Workbox

Workbox menyediakan cara mudah untuk mengirim pesan ke pekerja layanan, melalui paket workbox-window, sekumpulan modul yang dimaksudkan untuk dijalankan dalam konteks jendela. Library ini adalah pelengkap paket Workbox lainnya yang berjalan di pekerja layanan.

Untuk mengomunikasikan halaman dengan pekerja layanan, pertama-tama dapatkan referensi objek Workbox ke pekerja layanan yang terdaftar:

const wb = new Workbox('/sw.js');
wb.register();

Kemudian, Anda dapat langsung mengirim pesan secara deklaratif, tanpa repot mendapatkan pendaftaran, memeriksa aktivasi, atau memikirkan API komunikasi yang mendasarinya:

wb.messageSW({"type": "PREFETCH", "payload": {"urls": ["/data1.json", "data2.json"]}}); });

Pekerja layanan menerapkan pengendali message untuk memproses pesan ini. API ini dapat menampilkan respons secara opsional, meskipun dalam kasus seperti ini, hal ini tidak diperlukan:

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PREFETCH') {
    // do something
  }
});

Menggunakan API browser

Jika library Workbox tidak cukup untuk kebutuhan Anda, berikut cara menerapkan komunikasi jendela ke pekerja layanan, menggunakan API browser.

postMessage API dapat digunakan untuk membuat mekanisme komunikasi satu arah dari halaman ke pekerja layanan.

Halaman memanggil postMessage() pada antarmuka pekerja layanan:

navigator.serviceWorker.controller.postMessage({
  type: 'MSG_ID',
  payload: 'some data to perform the task',
});

Pekerja layanan menerapkan pengendali message untuk memproses pesan ini.

self.addEventListener('message', (event) => {
  if (event.data && event.data.type === MSG_ID) {
    // do something
  }
});

Atribut {type : 'MSG_ID'} tidak mutlak diperlukan, tetapi ini adalah salah satu cara untuk mengizinkan halaman mengirim berbagai jenis petunjuk ke pekerja layanan (yaitu, 'untuk mengambil data' vs. 'menghapus penyimpanan'). Pekerja layanan dapat bercabang ke dalam jalur eksekusi yang berbeda berdasarkan flag ini.

Jika operasi berhasil, pengguna akan bisa mendapatkan manfaatnya tetapi, jika tidak, itu tidak akan mengubah alur pengguna utama. Misalnya, saat 1-800-Flowers.com mencoba melakukan precache, halaman tidak perlu mengetahui apakah pekerja layanan berhasil. Jika ya, maka pengguna akan menikmati navigasi yang lebih cepat. Jika tidak, halaman masih harus membuka halaman baru. Hanya butuh waktu sedikit lebih lama.

Contoh pengambilan data sederhana

Salah satu penerapan yang paling umum dari cache imperatif adalah pengambilan data, yang berarti mengambil resource untuk URL tertentu, sebelum pengguna berpindah ke URL tersebut, untuk mempercepat navigasi.

Ada berbagai cara untuk menerapkan pengambilan data di situs:

  • Menggunakan tag pengambilan link di halaman: resource disimpan di cache browser selama lima menit, setelah itu aturan Cache-Control normal untuk resource akan diterapkan.
  • Melengkapi teknik sebelumnya dengan strategi caching runtime di pekerja layanan untuk memperpanjang masa pakai resource pengambilan data melebihi batas ini.

Untuk skenario pengambilan data yang relatif sederhana, seperti pengambilan data dokumen, atau aset tertentu (JS, CSS, dll.), teknik tersebut adalah pendekatan terbaik.

Jika logika tambahan diperlukan, misalnya, mengurai resource pengambilan data (halaman atau file JSON) untuk mengambil URL internalnya, sebaiknya delegasikan tugas ini sepenuhnya ke pekerja layanan.

Mendelegasikan jenis operasi ini ke pekerja layanan memiliki keuntungan berikut:

  • Mengurangi beban tugas pengambilan dan pemrosesan pasca-pengambilan (yang akan dijelaskan nanti) ke thread sekunder. Dengan begitu, thread utama akan dikosongkan untuk menangani tugas yang lebih penting seperti merespons interaksi pengguna.
  • Memungkinkan beberapa klien (misalnya, tab) untuk menggunakan kembali fungsi umum, dan bahkan memanggil layanan secara bersamaan tanpa memblokir thread utama.

Lakukan pra-pengambilan halaman detail produk

Pertama-tama, gunakan postMessage() di antarmuka pekerja layanan dan teruskan array URL ke cache:

navigator.serviceWorker.controller.postMessage({
  type: 'PREFETCH',
  payload: {
    urls: [
      'www.exmaple.com/apis/data_1.json',
      'www.exmaple.com/apis/data_2.json',
    ],
  },
});

Pada pekerja layanan, terapkan pengendali message untuk menangkap dan memproses pesan yang dikirim oleh tab aktif:

addEventListener('message', (event) => {
  let data = event.data;
  if (data && data.type === 'PREFETCH') {
    let urls = data.payload.urls;
    for (let i in urls) {
      fetchAsync(urls[i]);
    }
  }
});

Dalam kode sebelumnya, kami memperkenalkan fungsi helper kecil yang disebut fetchAsync() untuk melakukan iterasi pada array URL dan mengeluarkan permintaan pengambilan untuk setiap URL:

async function fetchAsync(url) {
  // await response of fetch call
  let prefetched = await fetch(url);
  // (optionally) cache resources in the service worker storage
}

Setelah respons diperoleh, Anda dapat mengandalkan header caching resource. Namun, sering kali, seperti di halaman detail produk, resource tidak di-cache (yang berarti, resource tersebut memiliki header Cache-control no-cache). Dalam kasus seperti ini, Anda dapat mengganti perilaku ini dengan menyimpan resource yang diambil di cache pekerja layanan. Hal ini memiliki manfaat tambahan yaitu memungkinkan file ditayangkan dalam skenario offline.

Lebih dari sekadar data JSON

Setelah diambil dari endpoint server, data JSON sering kali berisi URL lain yang juga layak untuk pengambilan data, seperti gambar atau data endpoint lain yang terkait dengan data level pertama ini.

Anggap saja dalam contoh kita, data JSON yang ditampilkan adalah informasi dari situs belanja bahan makanan:

{
  "productName": "banana",
  "productPic": "https://cdn.example.com/product_images/banana.jpeg",
  "unitPrice": "1.99"
 }

Ubah kode fetchAsync() untuk melakukan iterasi pada daftar produk dan meng-cache banner besar untuk masing-masing produk:

async function fetchAsync(url, postProcess) {
  // await response of fetch call
  let prefetched = await fetch(url);

  //(optionally) cache resource in the service worker cache

  // carry out the post fetch process if supplied
  if (postProcess) {
    await postProcess(prefetched);
  }
}

async function postProcess(prefetched) {
  let productJson = await prefetched.json();
  if (productJson && productJson.product_pic) {
    fetchAsync(productJson.product_pic);
  }
}

Anda dapat menambahkan beberapa penanganan pengecualian di sekitar kode ini untuk situasi seperti 404. Namun, keunggulan menggunakan pekerja layanan untuk pengambilan data adalah tindakan tersebut dapat gagal tanpa banyak konsekuensi terhadap halaman dan thread utama. Anda mungkin juga memiliki logika yang lebih rumit dalam pascapemrosesan konten yang diambil data, sehingga konten tersebut lebih fleksibel dan terpisah dengan data yang ditanganinya. Tak ada yang membatasimu.

Kesimpulan

Dalam artikel ini, kami membahas kasus penggunaan umum komunikasi satu arah antara halaman dan pekerja layanan: cache imperatif. Contoh yang dibahas hanya dimaksudkan untuk menunjukkan satu cara menggunakan pola ini. Pendekatan yang sama juga dapat diterapkan pada kasus penggunaan lainnya, misalnya menyimpan artikel teratas dalam cache sesuai permintaan untuk penggunaan offline, bookmark, dan lainnya.

Untuk pola komunikasi halaman dan pekerja layanan lainnya, lihat:

  • Menyiarkan update: Memanggil halaman dari pekerja layanan untuk menginformasikan update penting (mis., versi webapp baru tersedia).
  • Komunikasi dua arah: Mendelegasikan tugas ke pekerja layanan (misalnya, download yang berat), dan terus memberi tahu halaman tentang progresnya.