Mengganti halaman latar belakang atau peristiwa dengan pekerja layanan
Pekerja layanan 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 dasar ekstensi sejak diperkenalkan. Sederhananya, halaman latar belakang menyediakan lingkungan yang tidak bergantung pada 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 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 pekerja layanan ekstensi yang disebut 'skrip latar belakang'. Meskipun pekerja layanan ekstensi berjalan di latar belakang, menyebutnya sebagai skrip latar belakang agak menyesatkan karena menyiratkan kemampuan yang identik. Perbedaannya dijelaskan di bawah ini.
Perubahan dari halaman latar belakang
Pekerja layanan memiliki sejumlah perbedaan dengan halaman latar belakang.
- Fungsi ini berfungsi di luar thread utama, yang berarti tidak mengganggu konten ekstensi.
- Ekstensi ini memiliki kemampuan khusus seperti mencegat peristiwa pengambilan di origin ekstensi, seperti peristiwa dari pop-up toolbar.
- Mereka 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. Untuk memulai, cara pekerja layanan 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()
, 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 ini secara mendetail.
Memperbarui kolom "background" dalam manifes
Di Manifest V3, halaman latar belakang diganti dengan pekerja layanan. Perubahan manifes tercantum di bawah.
- Ganti
"background.scripts"
dengan"background.service_worker"
dimanifest.json
. Perhatikan bahwa kolom"service_worker"
menggunakan string, bukan array string. - Menghapus
"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 DOM dan objek 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 lain, tetapi berfungsi sebagai halaman web lengkap untuk berinteraksi dengan ekstensi.
Untuk menggunakan Offscreen API, buat dokumen di balik layar dari pekerja layanan.
chrome.offscreen.createDocument({
url: chrome.runtime.getURL('offscreen.html'),
reasons: ['CLIPBOARD'],
justification: 'testing the offscreen API',
});
Dalam dokumen di balik 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 pengiriman 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 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 balik layar dengan rutinitas konversi dan pengendali
runtime.onMessage
. - Tambahkan rutinitas konversi ke dokumen di balik layar.
- Di worker layanan ekstensi, periksa
chrome.storage
untuk data Anda. - Jika data Anda tidak ditemukan, create dokumen di luar layar dan panggil
runtime.sendMessage()
untuk memulai rutinitas konversi. - Di pengendali
runtime.onMessage
yang Anda tambahkan ke dokumen di balik layar, panggil rutinitas 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);
});
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 didaftarkan (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.
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 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
Pekerja layanan bersifat sementara, yang berarti pekerja layanan 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 dihapus. Untuk mengatasi hal ini, gunakan API penyimpanan sebagai sumber tepercaya. Contoh akan menunjukkan cara melakukannya.
Contoh berikut menggunakan variabel global untuk menyimpan nama. Di pekerja layanan, 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 yang 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(() => { 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 pekerja layanan
Service worker secara definisi berbasis peristiwa dan akan dihentikan jika tidak ada aktivitas. Dengan demikian, Chrome dapat mengoptimalkan performa dan konsumsi memori ekstensi Anda. Pelajari lebih lanjut di dokumentasi siklus proses pekerja layanan kami. Kasus khusus mungkin memerlukan tindakan tambahan untuk memastikan bahwa pekerja layanan tetap aktif untuk waktu yang lebih lama.
Mempertahankan service worker tetap aktif hingga operasi yang berjalan lama selesai
Selama operasi pekerja layanan yang berjalan lama dan tidak memanggil API ekstensi, pekerja layanan mungkin dinonaktifkan di tengah operasi. Contohnya mencakup:
- Permintaan
fetch()
berpotensi memerlukan waktu lebih dari lima menit (misalnya, download besar pada koneksi yang berpotensi buruk). - Penghitungan asinkron yang kompleks yang memerlukan waktu lebih dari 30 detik.
Untuk memperpanjang masa aktif pekerja layanan dalam kasus ini, Anda dapat memanggil API ekstensi biasa secara berkala untuk mereset penghitung waktu tunggu. Perhatikan bahwa ini hanya dicadangkan 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 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 pekerja layanan 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 mengizinkannya di sana, tetapi kami tidak mendukungnya secara umum. Dalam keadaan luar biasa ini, mempertahankan pekerja layanan tetap aktif dapat dilakukan dengan memanggil API ekstensi yang tidak penting 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 agar pekerja layanan 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 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'];
}