Dalam beberapa kasus, aplikasi web mungkin perlu membuat saluran komunikasi dua arah antara halaman dan pekerja layanan.
Misalnya: di PWA podcast, seseorang dapat membuat fitur untuk memungkinkan pengguna mendownload episode untuk konsumsi offline dan memungkinkan pekerja layanan untuk terus memberi tahu halaman tentang progres secara berkala, sehingga thread utama dapat mengupdate UI.
Dalam panduan ini, kita akan mempelajari berbagai cara untuk menerapkan komunikasi dua arah antara konteks Window dan pekerja layanan, dengan mempelajari berbagai API, library Workbox, serta beberapa kasus lanjutan.
Menggunakan Workbox
workbox-window
adalah kumpulan
modul library Workbox yang dimaksudkan
untuk dijalankan dalam konteks jendela. Class Workbox
menyediakan metode messageSW()
untuk mengirim pesan ke pekerja layanan terdaftar instance dan
menunggu respons.
Kode halaman berikut membuat instance Workbox
baru dan mengirim pesan ke pekerja layanan
untuk mendapatkan versinya:
const wb = new Workbox('/sw.js');
wb.register();
const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);
Pekerja layanan menerapkan pemroses pesan di ujung lain, dan merespons pekerja layanan yang terdaftar:
const SW_VERSION = '1.0.0';
self.addEventListener('message', (event) => {
if (event.data.type === 'GET_VERSION') {
event.ports[0].postMessage(SW_VERSION);
}
});
Di balik layar, library ini menggunakan API browser yang akan kita tinjau di bagian berikutnya: Message Channel, tetapi memisahkan banyak detail implementasi, sehingga lebih mudah digunakan, sekaligus memanfaatkan dukungan browser yang luas yang dimiliki API ini.
Menggunakan Browser API
Jika library Workbox tidak cukup untuk kebutuhan Anda, ada beberapa API tingkat rendah yang tersedia untuk menerapkan komunikasi "dua arah" antara halaman dan pekerja layanan. Keduanya memiliki beberapa kesamaan dan perbedaan:
Persamaan:
- Dalam semua kasus, komunikasi dimulai di satu ujung melalui antarmuka
postMessage()
dan diterima di ujung lainnya dengan menerapkan pengendalimessage
. - Dalam praktiknya, semua API yang tersedia memungkinkan kita menerapkan kasus penggunaan yang sama, tetapi beberapa di antaranya mungkin menyederhanakan pengembangan dalam beberapa skenario.
Perbedaan:
- Keduanya memiliki cara yang berbeda untuk mengidentifikasi sisi lain komunikasi: beberapa di antaranya menggunakan referensi eksplisit ke konteks lain, sementara yang lain dapat berkomunikasi secara implisit melalui objek proxy yang dibuat instance-nya di setiap sisi.
- Dukungan browser bervariasi di antara browser tersebut.
Broadcast Channel API
Broadcast Channel API memungkinkan komunikasi dasar antara konteks penjelajahan melalui objek BroadcastChannel.
Untuk menerapkannya, pertama-tama, setiap konteks harus membuat instance objek BroadcastChannel
dengan ID yang sama
dan mengirim serta menerima pesan darinya:
const broadcast = new BroadcastChannel('channel-123');
Objek BroadcastChannel mengekspos antarmuka postMessage()
untuk mengirim pesan ke konteks pemrosesan
apa pun:
//send message
broadcast.postMessage({ type: 'MSG_ID', });
Setiap konteks browser dapat memproses pesan melalui metode onmessage
dari objek
BroadcastChannel
:
//listen to messages
broadcast.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process message...
}
};
Seperti yang terlihat, tidak ada referensi eksplisit ke konteks tertentu, sehingga tidak perlu mendapatkan referensi terlebih dahulu ke pekerja layanan atau klien tertentu.
Kelemahannya adalah, pada saat penulisan ini, API memiliki dukungan dari Chrome, Firefox, dan Edge, tetapi browser lain, seperti Safari, belum mendukungnya.
Client API
Client API memungkinkan Anda mendapatkan
referensi ke semua objek WindowClient
yang mewakili tab aktif yang dikontrol oleh pekerja layanan.
Karena halaman dikontrol oleh satu pekerja layanan, halaman akan memproses dan mengirim pesan ke pekerja layanan aktif secara langsung melalui antarmuka serviceWorker
:
//send message
navigator.serviceWorker.controller.postMessage({
type: 'MSG_ID',
});
//listen to messages
navigator.serviceWorker.onmessage = (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//process response
}
};
Demikian pula, pekerja layanan memproses pesan dengan menerapkan pemroses onmessage
:
//listen to messages
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'MSG_ID') {
//Process message
}
});
Untuk berkomunikasi kembali dengan kliennya, pekerja layanan mendapatkan array
objek WindowClient
dengan menjalankan
metode seperti
Clients.matchAll()
dan
Clients.get()
. Kemudian, postMessage()
dapat
melakukan salah satu tindakan berikut:
//Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
if (clients && clients.length) {
//Respond to last focused tab
clients[0].postMessage({type: 'MSG_ID'});
}
});
Client API
adalah opsi yang baik untuk berkomunikasi dengan mudah dengan semua tab aktif dari pekerja layanan
dengan cara yang relatif mudah. API ini didukung oleh semua browser utama, tetapi tidak semua metodenya mungkin tersedia. Jadi, pastikan untuk memeriksa dukungan browser sebelum menerapkannya di situs Anda.
Saluran Pesan
Saluran Pesan memerlukan menentukan dan meneruskan port dari satu konteks ke konteks lain untuk membuat saluran komunikasi dua arah.
Untuk melakukan inisialisasi saluran, halaman membuat instance objek MessageChannel
dan menggunakannya
untuk mengirim port ke pekerja layanan terdaftar. Halaman ini juga menerapkan pemroses onmessage
untuk
menerima pesan dari konteks lain:
const messageChannel = new MessageChannel();
//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
messageChannel.port2,
]);
//Listen to messages
messageChannel.port1.onmessage = (event) => {
// Process message
};
Pekerja layanan menerima port, menyimpan referensi ke port tersebut, dan menggunakannya untuk mengirim pesan ke sisi lain:
let communicationPort;
//Save reference to port
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'PORT_INITIALIZATION') {
communicationPort = event.ports[0];
}
});
//Send messages
communicationPort.postMessage({type: 'MSG_ID'});
MessageChannel
saat ini didukung oleh semua browser utama.
API Lanjutan: Sinkronisasi Latar Belakang dan Pengambilan Latar Belakang
Dalam panduan ini, kita telah mempelajari cara menerapkan teknik komunikasi dua arah, untuk kasus yang relatif sederhana, seperti meneruskan pesan string yang menjelaskan operasi yang akan dilakukan, atau daftar URL untuk di-cache dari satu konteks ke konteks lainnya. Di bagian ini, kita akan mempelajari dua API untuk menangani skenario tertentu: kurangnya konektivitas dan download yang lama.
Sinkronisasi Latar Belakang
Aplikasi chat mungkin ingin memastikan bahwa pesan tidak pernah hilang karena konektivitas yang buruk. Background Sync API memungkinkan Anda menunda tindakan untuk dicoba lagi saat pengguna memiliki konektivitas yang stabil. Hal ini berguna untuk memastikan bahwa apa pun yang ingin dikirim pengguna, benar-benar dikirim.
Halaman mendaftarkan sync
, bukan antarmuka postMessage()
:
navigator.serviceWorker.ready.then(function (swRegistration) {
return swRegistration.sync.register('myFirstSync');
});
Pekerja layanan kemudian memproses peristiwa sync
untuk memproses pesan:
self.addEventListener('sync', function (event) {
if (event.tag == 'myFirstSync') {
event.waitUntil(doSomeStuff());
}
});
Fungsi doSomeStuff()
harus menampilkan promise yang menunjukkan keberhasilan/kegagalan dari apa pun yang
dicoba. Jika terpenuhi, sinkronisasi akan selesai. Jika gagal, sinkronisasi lain akan dijadwalkan untuk
mencoba lagi. Sinkronisasi coba lagi juga menunggu konektivitas, dan menggunakan backoff eksponensial.
Setelah operasi dilakukan, pekerja layanan kemudian dapat berkomunikasi kembali dengan halaman untuk memperbarui UI, dengan menggunakan salah satu API komunikasi yang dijelajahi sebelumnya.
Penelusuran Google menggunakan Sinkronisasi Latar Belakang untuk mempertahankan kueri yang gagal karena konektivitas yang buruk, dan mencobanya lagi nanti saat pengguna online. Setelah operasi dilakukan, keduanya akan menyampaikan hasilnya kepada pengguna melalui notifikasi push web:
Pengambilan Latar Belakang
Untuk pekerjaan yang relatif singkat seperti mengirim pesan, atau daftar URL untuk di-cache, opsi yang telah dijelajahi sejauh ini adalah pilihan yang baik. Jika tugas memerlukan waktu terlalu lama, browser akan menghentikan pekerja layanan. Jika tidak, hal ini akan berisiko terhadap privasi dan baterai pengguna.
Background Fetch API memungkinkan Anda memindahkan tugas yang lama ke pekerja layanan, seperti mendownload film, podcast, atau level game.
Untuk berkomunikasi dengan pekerja layanan dari halaman, gunakan backgroundFetch.fetch
, bukan
postMessage()
:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.fetch(
'my-fetch',
['/ep-5.mp3', 'ep-5-artwork.jpg'],
{
title: 'Episode 5: Interesting things.',
icons: [
{
sizes: '300x300',
src: '/ep-5-icon.png',
type: 'image/png',
},
],
downloadTotal: 60 * 1024 * 1024,
},
);
});
Objek BackgroundFetchRegistration
memungkinkan halaman memproses peristiwa progress
untuk mengikuti
progres download:
bgFetch.addEventListener('progress', () => {
// If we didn't provide a total, we can't provide a %.
if (!bgFetch.downloadTotal) return;
const percent = Math.round(
(bgFetch.downloaded / bgFetch.downloadTotal) * 100,
);
console.log(`Download progress: ${percent}%`);
});
Langkah berikutnya
Dalam panduan ini, kita akan mempelajari kasus komunikasi paling umum antara pekerja halaman dan layanan (komunikasi dua arah).
Sering kali, satu komponen mungkin hanya memerlukan satu konteks untuk berkomunikasi dengan komponen lainnya, tanpa menerima respons. Lihat panduan berikut untuk mendapatkan panduan cara menerapkan teknik searah di halaman Anda dari dan ke pekerja layanan, beserta kasus penggunaan dan contoh produksi:
- Panduan penyimpanan dalam cache imperatif: Memanggil pekerja layanan dari halaman untuk menyimpan resource dalam cache terlebih dahulu (misalnya, dalam skenario pengambilan data sebelumnya).
- Menyebarkan update: Memanggil halaman dari pekerja layanan untuk menginformasikan update penting (misalnya, versi baru webapp tersedia).