Mengganti halaman latar belakang atau peristiwa dengan pekerja layanan
Pekerja layanan menggantikan halaman latar belakang atau peristiwa ekstensi untuk memastikan bahwa kode latar belakang tetap berada di luar thread utama. Hal ini memungkinkan ekstensi berjalan hanya saat diperlukan, sehingga menghemat resource.
Halaman latar belakang telah menjadi komponen dasar ekstensi sejak diperkenalkan. Sederhananya, halaman latar belakang menyediakan lingkungan yang berjalan secara independen dari jendela atau tab lain. Hal ini memungkinkan ekstensi mengamati dan bertindak sebagai respons terhadap peristiwa.
Halaman ini menjelaskan tugas untuk mengonversi halaman latar belakang menjadi pekerja layanan ekstensi. Untuk mengetahui informasi selengkapnya tentang pekerja layanan ekstensi secara umum, lihat tutorial Menangani peristiwa dengan pekerja layanan dan bagian Tentang pekerja layanan ekstensi.
Perbedaan antara skrip latar belakang dan pekerja layanan ekstensi
Dalam beberapa konteks, Anda akan melihat service worker ekstensi yang disebut 'skrip latar belakang'. Meskipun service worker ekstensi berjalan di latar belakang, menyebutnya sebagai skrip latar belakang agak menyesatkan karena menyiratkan kemampuan yang sama. Perbedaannya dijelaskan di bawah ini.
Perubahan dari halaman latar belakang
Service worker memiliki sejumlah perbedaan dengan halaman latar belakang.
- Service worker berfungsi di luar thread utama, yang berarti tidak mengganggu konten ekstensi.
- Service worker memiliki kemampuan khusus seperti menangkap peristiwa pengambilan di origin ekstensi, seperti yang berasal dari pop-up toolbar.
- Service worker dapat berkomunikasi dan berinteraksi dengan konteks lain melalui antarmuka Klien.
Perubahan yang perlu Anda lakukan
Anda harus melakukan beberapa penyesuaian kode untuk memperhitungkan perbedaan antara cara kerja skrip latar belakang dan pekerja layanan. Pertama, cara service worker ditentukan dalam file manifes berbeda dengan cara skrip latar belakang ditentukan. Selain itu:
- Karena tidak dapat mengakses DOM atau antarmuka
window, Anda harus memindahkan panggilan tersebut ke API lain atau ke dokumen di luar layar. - Pemroses peristiwa tidak boleh didaftarkan sebagai respons terhadap promise yang ditampilkan atau di dalam callback peristiwa.
- Karena tidak kompatibel dengan versi lama
XMLHttpRequest(), Anda harus mengganti panggilan ke antarmuka ini dengan panggilan kefetch(). - Karena dihentikan saat tidak digunakan, Anda harus mempertahankan status aplikasi, bukan mengandalkan variabel global. Menghentikan pekerja layanan juga dapat mengakhiri timer sebelum selesai. Anda harus menggantinya dengan alarm.
Halaman ini menjelaskan tugas-tugas tersebut secara mendetail.
Memperbarui kolom "background" dalam manifes
Di Manifes V3, halaman latar belakang diganti dengan service worker. Perubahan manifes tercantum di bawah.
- Ganti
"background.scripts"dengan"background.service_worker"dimanifest.json. Perhatikan bahwa kolom"service_worker"menggunakan string, bukan array string. - Hapus
"background.persistent"darimanifest.json.
{ ... "background": { "scripts": [ "backgroundContextMenus.js", "backgroundOauth.js" ], "persistent": false }, ... }
{ ... "background": { "service_worker": "service_worker.js", "type": "module" } ... }
Kolom "service_worker" menggunakan satu string. Anda hanya memerlukan kolom "type" jika menggunakan modul ES (menggunakan kata kunci import). Nilainya akan selalu "module". Untuk mengetahui informasi selengkapnya, lihat Dasar-dasar pekerja layanan ekstensi
Memindahkan panggilan DOM dan jendela ke dokumen di luar layar
Beberapa ekstensi memerlukan akses ke objek DOM dan jendela tanpa membuka jendela atau tab baru secara visual. Offscreen API mendukung kasus penggunaan ini dengan membuka dan menutup dokumen yang tidak ditampilkan yang dipaketkan dengan ekstensi, tanpa mengganggu pengalaman pengguna. Kecuali untuk penerusan pesan, dokumen di luar layar tidak berbagi API dengan konteks ekstensi lainnya, tetapi berfungsi sebagai halaman web lengkap yang dapat digunakan ekstensi untuk berinteraksi.
Untuk menggunakan Offscreen API, buat dokumen di balik layar dari service worker.
browser.offscreen.createDocument({
url: browser.runtime.getURL('offscreen.html'),
reasons: ['CLIPBOARD'],
justification: 'testing the offscreen API',
});
Dalam dokumen di luar layar, lakukan tindakan apa pun yang sebelumnya Anda jalankan dalam skrip latar belakang. Misalnya, Anda dapat menyalin teks yang dipilih di halaman host.
let textEl = document.querySelector('#text');
textEl.value = data;
textEl.select();
document.execCommand('copy');
Berkomunikasi antara dokumen di luar layar dan pekerja layanan ekstensi menggunakan penerusan pesan.
Mengonversi localStorage ke jenis lain
Antarmuka Storage platform web (dapat diakses dari window.localStorage) tidak dapat digunakan di pekerja layanan. Untuk mengatasinya, lakukan salah satu dari dua hal berikut. Pertama, Anda dapat menggantinya dengan panggilan ke mekanisme penyimpanan lain. Namespace browser.storage.local akan melayani sebagian besar kasus penggunaan, tetapi opsi lain juga tersedia.
Anda juga dapat memindahkan panggilannya ke dokumen di luar layar. Misalnya, untuk memigrasikan data yang sebelumnya disimpan di localStorage ke mekanisme lain:
- Buat dokumen di luar layar dengan rutinitas konversi dan pengendali
runtime.onMessage. - Tambahkan rutinitas konversi ke dokumen di balik layar.
- Di pekerja layanan ekstensi, periksa
browser.storageuntuk data Anda. - Jika data Anda tidak ditemukan, buat dokumen di luar layar dan panggil
runtime.sendMessage()untuk memulai rutinitas konversi. - Di pengendali
runtime.onMessageyang Anda tambahkan ke dokumen di luar layar, panggil rutinitas konversi.
Ada juga beberapa perbedaan dalam cara kerja API penyimpanan web di ekstensi. Pelajari lebih lanjut di Penyimpanan dan Cookie.
Mendaftarkan pemroses secara sinkron
Mendaftarkan pemroses secara asinkron (misalnya di dalam promise atau callback) tidak dijamin akan berfungsi di Manifes V3. Perhatikan kode berikut.
browser.storage.local.get(["badgeText"], ({ badgeText }) => {
browser.browserAction.setBadgeText({ text: badgeText });
browser.browserAction.onClicked.addListener(handleActionClick);
});
Hal ini berfungsi dengan halaman latar belakang persisten karena halaman terus berjalan dan tidak pernah diinisialisasi ulang. Di Manifes V3, pekerja layanan akan diinisialisasi ulang saat peristiwa dikirim. Artinya, saat peristiwa diaktifkan, pemroses tidak akan terdaftar (karena ditambahkan secara asinkron), dan peristiwa akan terlewat.
Sebagai gantinya, pindahkan pendaftaran pemroses peristiwa ke tingkat teratas skrip Anda. Hal ini memastikan bahwa Chrome akan dapat segera menemukan dan memanggil pengendali klik tindakan Anda, meskipun ekstensi Anda belum selesai menjalankan logika startup-nya.
browser.action.onClicked.addListener(handleActionClick);
browser.storage.local.get(["badgeText"], ({ badgeText }) => {
browser.action.setBadgeText({ text: badgeText });
});
Mengganti XMLHttpRequest() dengan fetch() global
XMLHttpRequest() tidak dapat dipanggil dari pekerja layanan, ekstensi, atau lainnya. Ganti panggilan dari skrip latar belakang Anda ke XMLHttpRequest() dengan panggilan ke fetch() global.
const xhr = new XMLHttpRequest(); console.log('UNSENT', xhr.readyState); xhr.open('GET', '/api', true); console.log('OPENED', xhr.readyState); xhr.onload = () => { console.log('DONE', xhr.readyState); }; xhr.send(null);
const response = await fetch('https://www.example.com/greeting.json'') console.log(response.statusText);
Mempertahankan status
Service worker bersifat sementara, yang berarti kemungkinan akan dimulai, dijalankan, dan dihentikan berulang kali selama sesi browser pengguna. Hal ini juga berarti bahwa data tidak langsung tersedia dalam variabel global karena konteks sebelumnya telah dihapus. Untuk mengatasi hal ini, gunakan API penyimpanan sebagai sumber kebenaran. Contoh berikut akan menunjukkan cara melakukannya.
Contoh berikut menggunakan variabel global untuk menyimpan nama. Dalam pekerja layanan, variabel ini dapat direset beberapa kali selama sesi browser pengguna.
let savedName = undefined; browser.runtime.onMessage.addListener(({ type, name }) => { if (type === "set-name") { savedName = name; } }); browser.browserAction.onClicked.addListener((tab) => { browser.tabs.sendMessage(tab.id, { name: savedName }); });
Untuk Manifes V3, ganti variabel global dengan panggilan ke Storage API.
browser.runtime.onMessage.addListener(({ type, name }) => { if (type === "set-name") { browser.storage.local.set({ name }); } }); browser.action.onClicked.addListener(async (tab) => { const { name } = await browser.storage.local.get(["name"]); browser.tabs.sendMessage(tab.id, { name }); });
Mengonversi timer menjadi alarm
Sangat umum untuk menggunakan operasi tertunda atau berkala menggunakan metode setTimeout() atau setInterval(). Namun, API ini dapat gagal di pekerja layanan, karena timer dibatalkan setiap kali pekerja layanan dihentikan.
// 3 minutes in milliseconds const TIMEOUT = 3 * 60 * 1000; setTimeout(() => { browser.action.setIcon({ path: getRandomIconPath(), }); }, TIMEOUT);
Sebagai gantinya, gunakan Alarms API. Seperti pemroses lainnya, pemroses alarm harus didaftarkan di tingkat teratas skrip Anda.
async function startAlarm(name, duration) { await browser.alarms.create(name, { delayInMinutes: 3 }); } browser.alarms.onAlarm.addListener(() => { browser.action.setIcon({ path: getRandomIconPath(), }); });
Memastikan pekerja layanan tetap aktif
Pekerja layanan, menurut definisinya, berbasis peristiwa dan akan dihentikan jika tidak ada aktivitas. Dengan cara ini, Chrome dapat mengoptimalkan performa dan konsumsi memori ekstensi Anda. Pelajari lebih lanjut di dokumentasi siklus proses pekerja layanan kami. Kasus luar biasa mungkin memerlukan tindakan tambahan untuk memastikan bahwa pekerja layanan tetap aktif untuk waktu yang lebih lama.
Membuat pekerja layanan tetap aktif hingga operasi yang berjalan lama selesai
Selama operasi pekerja layanan yang berjalan lama dan tidak memanggil API ekstensi, pekerja layanan mungkin akan berhenti di tengah operasi. Contohnya mencakup:
- Permintaan
fetch()yang berpotensi memerlukan waktu lebih dari lima menit (misalnya, download berukuran besar pada koneksi yang berpotensi buruk). - Penghitungan asinkron yang kompleks dan memerlukan waktu lebih dari 30 detik.
Untuk memperpanjang masa aktif pekerja layanan dalam kasus ini, Anda dapat memanggil API ekstensi sederhana secara berkala untuk mereset penghitung waktu tunggu. Perlu diperhatikan bahwa hal ini hanya berlaku untuk kasus luar biasa dan dalam sebagian besar situasi, biasanya ada cara idiomatis platform yang lebih baik untuk mencapai hasil yang sama.
Contoh berikut menunjukkan fungsi bantuan waitUntil() yang membuat pekerja layanan Anda tetap aktif hingga promise tertentu diselesaikan:
async function waitUntil(promise) {
const keepAlive = setInterval(browser.runtime.getPlatformInfo, 25 * 1000);
try {
await promise;
} finally {
clearInterval(keepAlive);
}
}
waitUntil(someExpensiveCalculation());
Membuat pekerja layanan tetap aktif secara terus-menerus
Dalam kasus yang jarang terjadi, masa aktif perlu diperpanjang tanpa batas waktu. Kami telah mengidentifikasi perusahaan dan pendidikan sebagai kasus penggunaan terbesar, dan kami secara khusus mengizinkan hal ini di sana, tetapi kami tidak mendukung hal ini secara umum. Dalam keadaan luar biasa ini, menjaga service worker tetap aktif dapat dilakukan dengan memanggil API ekstensi sederhana secara berkala. Perlu diperhatikan bahwa rekomendasi ini hanya berlaku untuk ekstensi yang berjalan di perangkat terkelola untuk kasus penggunaan perusahaan atau pendidikan. Hal ini tidak diizinkan dalam kasus lain dan tim ekstensi Chrome berhak mengambil tindakan terhadap ekstensi tersebut pada masa mendatang.
Gunakan cuplikan kode berikut untuk menjaga service worker tetap aktif:
/**
* Tracks when a service worker was last alive and extends the service worker
* lifetime by writing the current time to extension storage every 20 seconds.
* You should still prepare for unexpected termination - for example, if the
* extension process crashes or your extension is manually stopped at
* chrome://serviceworker-internals.
*/
let heartbeatInterval;
async function runHeartbeat() {
await browser.storage.local.set({ 'last-heartbeat': new Date().getTime() });
}
/**
* Starts the heartbeat interval which keeps the service worker alive. Call
* this sparingly when you are doing work which requires persistence, and call
* stopHeartbeat once that work is complete.
*/
async function startHeartbeat() {
// Run the heartbeat once at service worker startup.
runHeartbeat().then(() => {
// Then again every 20 seconds.
heartbeatInterval = setInterval(runHeartbeat, 20 * 1000);
});
}
async function stopHeartbeat() {
clearInterval(heartbeatInterval);
}
/**
* Returns the last heartbeat stored in extension storage, or undefined if
* the heartbeat has never run before.
*/
async function getLastHeartbeat() {
return (await browser.storage.local.get('last-heartbeat'))['last-heartbeat'];
}