Panduan cache imperatif

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

Beberapa situs mungkin perlu berkomunikasi dengan pekerja layanan tanpa perlu diberi tahu tentang hasilnya. Berikut beberapa contohnya:

  • Halaman mengirimkan daftar URL kepada pekerja layanan untuk di-prefetch, sehingga, saat pengguna mengklik link, sub-resource dokumen atau halaman sudah tersedia di cache, sehingga navigasi berikutnya jauh lebih cepat.
  • Halaman meminta pekerja layanan untuk mengambil dan meng-cache sekumpulan artikel teratas, agar artikel tersebut tersedia untuk tujuan offline.

Mendelegasikan jenis tugas non-penting ini ke pekerja layanan memiliki manfaat untuk mengosongkan thread utama agar dapat menangani tugas yang lebih mendesak dengan lebih baik, seperti merespons interaksi pengguna.

Diagram halaman yang meminta resource untuk di-cache ke pekerja layanan.

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 caching imperatif.

Kasus produksi

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

Logo 1-800 Flowers.

Mereka menggunakan pendekatan campuran untuk menentukan item mana yang akan dipramuat:

  • 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 tersebut memproses peristiwa mouseover , sehingga, saat pengguna memindahkan kursor ke atas item, mereka dapat memicu pengambilan resource secara "on demand".

API ini menggunakan Cache API untuk menyimpan respons JSON:

Logo 1-800 Flowers.
Mengambil data produk JSON terlebih dahulu dari halaman listingan produk di 1-800Flowers.com.

Saat pengguna mengklik item, data JSON yang terkait dengannya dapat diambil dari cache, tanpa perlu membuka jaringan, sehingga navigasi menjadi lebih cepat.

Menggunakan Workbox

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

Untuk berkomunikasi dengan halaman menggunakan 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 perlu 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. Secara opsional, metode ini dapat menampilkan respons, 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 pekerja jendela ke layanan, menggunakan API browser.

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

Halaman memanggil postMessage() di 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 melakukan pengambilan data sebelumnya' vs. 'untuk menghapus penyimpanan'). Pekerja layanan dapat bercabang ke jalur eksekusi yang berbeda berdasarkan tanda ini.

Jika operasi berhasil, pengguna akan dapat memperoleh manfaat darinya, tetapi jika tidak, operasi tersebut tidak akan mengubah alur penggunaan utama. Misalnya, saat 1-800-Flowers.com mencoba melakukan pra-cache, halaman tidak perlu mengetahui apakah pekerja layanan berhasil. Jika demikian, pengguna akan menikmati navigasi yang lebih cepat. Jika tidak, halaman masih harus membuka halaman baru. Hanya memerlukan waktu sedikit lebih lama.

Contoh pengambilan data awal yang sederhana

Salah satu aplikasi caching imperatif yang paling umum adalah prefetching, yang berarti mengambil resource untuk URL tertentu, sebelum pengguna beralih ke URL tersebut, untuk mempercepat navigasi.

Ada berbagai cara untuk menerapkan pengambilan data di situs:

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 (file atau halaman JSON) untuk mengambil URL internalnya, sebaiknya delegasikan tugas ini sepenuhnya ke pekerja layanan.

Mendelegasikan jenis operasi ini ke pekerja layanan memiliki keuntungan berikut:

  • Mengalihkan beban berat pengambilan dan pemrosesan pasca-pengambilan (yang akan diperkenalkan nanti) ke thread sekunder. Dengan melakukan hal ini, thread utama akan bebas untuk menangani tugas yang lebih penting seperti merespons interaksi pengguna.
  • Mengizinkan beberapa klien (misalnya tab) untuk menggunakan kembali fungsi umum, dan bahkan memanggil layanan secara bersamaan tanpa memblokir thread utama.

Mengambil halaman detail produk terlebih dahulu

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

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

Di pekerja layanan, terapkan pengendali message untuk mencegat 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]);
    }
  }
});

Pada kode sebelumnya, kita memperkenalkan fungsi bantuan 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
}

Saat respons diperoleh, Anda dapat mengandalkan header cache resource. Namun, dalam banyak kasus, seperti di halaman detail produk, resource tidak di-cache (yang berarti, resource memiliki header Cache-control dari 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 untuk memungkinkan file ditayangkan dalam skenario offline.

Di luar data JSON

Setelah diambil dari endpoint server, data JSON sering kali berisi URL lain yang juga layak di-prefetch, seperti gambar atau data endpoint lainnya yang terkait dengan data tingkat pertama ini.

Misalnya, dalam contoh ini, data JSON yang ditampilkan adalah informasi 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 menyimpan gambar hero ke dalam cache untuk setiap 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 melakukan pengambilan data sebelumnya adalah pekerja layanan dapat gagal tanpa banyak akibat pada halaman dan thread utama. Anda juga dapat memiliki logika yang lebih rumit dalam pascapemrosesan konten yang diambil datanya, sehingga 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 pekerja halaman dan layanan: caching imperatif. Contoh yang dibahas hanya dimaksudkan untuk menunjukkan salah satu cara menggunakan pola ini dan pendekatan yang sama juga dapat diterapkan ke kasus penggunaan lainnya, misalnya, meng-cache artikel teratas sesuai permintaan untuk konsumsi offline, bookmark, dan lainnya.

Untuk pola komunikasi halaman dan pekerja layanan lainnya, lihat:

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