Mengganti halaman latar belakang atau peristiwa dengan service worker
Service worker menggantikan halaman latar belakang atau peristiwa ekstensi untuk memastikan 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 mendasar ekstensi sejak diperkenalkan. Sederhananya, halaman latar belakang menyediakan lingkungan yang berjalan secara independen dari jendela atau tab lainnya. Hal ini memungkinkan ekstensi mengamati dan bertindak sebagai respons terhadap peristiwa.
Halaman ini menjelaskan tugas untuk mengonversi halaman latar belakang menjadi service worker ekstensi. Untuk mengetahui informasi selengkapnya tentang service worker ekstensi secara umum, lihat tutorial Menangani peristiwa dengan service worker dan bagian Tentang service worker ekstensi.
Perbedaan antara skrip latar belakang dan service worker ekstensi
Dalam beberapa konteks, Anda akan melihat service worker ekstensi disebut 'skrip latar belakang'. Meskipun service worker ekstensi berjalan di latar belakang, menyebutnya skrip latar belakang agak menyesatkan karena menyiratkan kemampuan yang identik. Perbedaannya dijelaskan di bawah ini.
Perubahan dari halaman latar belakang
Service worker memiliki sejumlah perbedaan dengan halaman latar belakang.
- Fungsi ini berjalan di luar thread utama, yang berarti tidak mengganggu konten ekstensi.
- Fungsi ini memiliki kemampuan khusus seperti mencegat peristiwa pengambilan di origin ekstensi, seperti dari pop-up toolbar.
- Fungsi ini 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 service worker. Sebagai permulaan, 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
XMLHttpRequest()versi sebelumnya, Anda harus mengganti panggilan ke antarmuka ini dengan panggilan kefetch(). - Karena akan dihentikan saat tidak digunakan, Anda harus mempertahankan status aplikasi, bukan mengandalkan variabel global. Menghentikan service worker juga dapat mengakhiri timer sebelum selesai. Anda harus menggantinya dengan alarm.
Halaman ini menjelaskan tugas-tugas ini secara mendetail.
Memperbarui kolom "background" di 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 berupa "module". Untuk mengetahui informasi selengkapnya, lihat Dasar-dasar service worker 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 dikemas 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 untuk interaksi ekstensi.
Untuk menggunakan Offscreen API, buat dokumen di luar layar dari service worker.
chrome.offscreen.createDocument({
url: chrome.runtime.getURL('offscreen.html'),
reasons: ['CLIPBOARD'],
justification: 'testing the offscreen API',
});
Dalam dokumen di luar layar, lakukan tindakan apa pun yang sebelumnya akan 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 service worker ekstensi menggunakan penerusan pesan.
Mengonversi localStorage ke jenis lain
Antarmuka Storage platform web (dapat diakses dari window.localStorage) tidak dapat digunakan di service worker. Untuk mengatasi hal ini, lakukan salah satu dari dua hal berikut. Pertama, Anda dapat menggantinya dengan panggilan ke mekanisme penyimpanan lain. Namespace chrome.storage.local akan melayani sebagian besar kasus penggunaan, tetapi opsi lain 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 rutin konversi dan pengendali
runtime.onMessage. - Tambahkan rutin konversi ke dokumen di luar layar.
- Di service worker ekstensi, periksa
chrome.storageuntuk data Anda. - Jika data Anda tidak ditemukan, buat dokumen di luar layar dan panggil
runtime.sendMessage()untuk memulai rutin konversi. - Di pengendali
runtime.onMessageyang Anda tambahkan ke dokumen di luar layar, panggil rutin konversi.
Ada juga beberapa nuansa terkait 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. Pertimbangkan kode berikut.
chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
chrome.browserAction.setBadgeText({ text: badgeText });
chrome.browserAction.onClicked.addListener(handleActionClick);
});
Kode ini berfungsi dengan halaman latar belakang persisten karena halaman terus berjalan dan tidak pernah diinisialisasi ulang. Di Manifes V3, service worker akan diinisialisasi ulang saat peristiwa dikirim. Artinya, saat peristiwa terjadi, pemroses tidak akan didaftarkan (karena ditambahkan secara asinkron), dan peristiwa akan terlewatkan.
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.
chrome.action.onClicked.addListener(handleActionClick);
chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
chrome.action.setBadgeText({ text: badgeText });
});
Mengganti XMLHttpRequest() dengan fetch() global
XMLHttpRequest() tidak dapat dipanggil dari service worker, ekstensi, atau lainnya. Ganti panggilan dari skrip latar belakang ke XMLHttpRequest() dengan panggilan ke global fetch().
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. Artinya, data tidak langsung tersedia dalam variabel global karena konteks sebelumnya telah dihentikan. Untuk mengatasinya, gunakan API penyimpanan sebagai sumber tepercaya. Contoh akan menunjukkan cara melakukannya.
Contoh berikut menggunakan variabel global untuk menyimpan nama. Di service worker, variabel ini dapat direset beberapa kali selama sesi browser pengguna.
let savedName = undefined; chrome.runtime.onMessage.addListener(({ type, name }) => { if (type === "set-name") { savedName = name; } }); chrome.browserAction.onClicked.addListener((tab) => { chrome.tabs.sendMessage(tab.id, { name: savedName }); });
Untuk Manifes V3, ganti variabel global dengan panggilan ke Storage API.
chrome.runtime.onMessage.addListener(({ type, name }) => { if (type === "set-name") { chrome.storage.local.set({ name }); } }); chrome.action.onClicked.addListener(async (tab) => { const { name } = await chrome.storage.local.get(["name"]); chrome.tabs.sendMessage(tab.id, { name }); });
Mengonversi timer menjadi alarm
Biasanya, operasi tertunda atau berkala digunakan menggunakan metode setTimeout() atau setInterval(). Namun, API ini dapat gagal di service worker karena timer dibatalkan setiap kali service worker dihentikan.
// 3 minutes in milliseconds const TIMEOUT = 3 * 60 * 1000; setTimeout(() => { chrome.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 chrome.alarms.create(name, { delayInMinutes: 3 }); } chrome.alarms.onAlarm.addListener(() => { chrome.action.setIcon({ path: getRandomIconPath(), }); });
Mempertahankan service worker
Service worker menurut definisi didorong oleh peristiwa dan akan dihentikan saat tidak ada aktivitas. Dengan cara ini, Chrome dapat mengoptimalkan performa dan penggunaan memori ekstensi Anda. Pelajari lebih lanjut di dokumentasi siklus proses service worker. Kasus luar biasa mungkin memerlukan langkah tambahan untuk memastikan service worker tetap aktif lebih lama.
Mempertahankan service worker hingga operasi yang berjalan lama selesai
Selama operasi service worker yang berjalan lama dan tidak memanggil API ekstensi, service worker mungkin akan dimatikan di tengah operasi. Contohnya mencakup:
- Permintaan
fetch()yang berpotensi memerlukan waktu lebih dari lima menit (misalnya, download besar pada koneksi yang berpotensi buruk). - Perhitungan asinkron yang kompleks memerlukan waktu lebih dari 30 detik.
Untuk memperpanjang masa aktif service worker dalam kasus ini, Anda dapat memanggil API ekstensi trivial secara berkala untuk mereset penghitung waktu tunggu. Perlu diperhatikan bahwa hal ini hanya disediakan untuk kasus luar biasa dan dalam sebagian besar situasi biasanya ada cara yang lebih baik dan idiomatis untuk mencapai hasil yang sama.
Contoh berikut menunjukkan fungsi helper waitUntil() yang membuat service worker Anda tetap aktif hingga promise tertentu diselesaikan:
async function waitUntil(promise) {
const keepAlive = setInterval(chrome.runtime.getPlatformInfo, 25 * 1000);
try {
await promise;
} finally {
clearInterval(keepAlive);
}
}
waitUntil(someExpensiveCalculation());
Mempertahankan service worker secara terus-menerus
Dalam kasus yang jarang terjadi, masa aktif perlu diperpanjang tanpa batas. Kami telah mengidentifikasi perusahaan dan pendidikan sebagai kasus penggunaan terbesar, dan kami secara khusus mengizinkannya di sana, tetapi kami tidak mendukungnya secara umum. Dalam situasi luar biasa ini, mempertahankan service worker dapat dilakukan dengan memanggil API ekstensi trivial secara berkala. Penting untuk 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 mempertahankan service worker Anda:
/**
* 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 chrome.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 chrome.storage.local.get('last-heartbeat'))['last-heartbeat'];
}