Bermigrasi ke pekerja layanan

Mengganti halaman latar belakang atau acara dengan pekerja layanan

Pekerja layanan menggantikan halaman peristiwa atau latar belakang ekstensi untuk memastikan bahwa kode latar belakang tetap berada di luar thread utama. Hal ini memungkinkan ekstensi hanya berjalan saat dibutuhkan, sehingga menghemat resource.

Halaman latar belakang telah menjadi komponen dasar ekstensi sejak diperkenalkan. Sederhananya, laman latar belakang menyediakan lingkungan yang hidup terlepas dari jendela atau tab lainnya. Hal ini memungkinkan ekstensi untuk mengamati dan bertindak dalam merespons 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 pekerja layanan ekstensi yang disebut 'skrip latar belakang'. Meskipun pekerja layanan ekstensi berjalan di latar belakang, memanggil skrip latar belakang agak menyesatkan dengan menyiratkan kemampuan yang identik. Perbedaannya dijelaskan di bawah ini.

Perubahan dari halaman latar belakang

Service worker memiliki sejumlah perbedaan dengan halaman latar belakang.

  • Fungsinya di luar thread utama, yang berarti tidak mengganggu konten ekstensi.
  • Fungsi ini memiliki kemampuan khusus seperti mencegat peristiwa pengambilan di asal ekstensi, seperti peristiwa dari pop-up toolbar.
  • Mereka dapat berkomunikasi dan berinteraksi dengan konteks lain melalui antarmuka Klien.

Perubahan yang harus dilakukan

Anda harus melakukan beberapa penyesuaian kode untuk memperhitungkan perbedaan antara cara kerja skrip latar belakang dan pekerja layanan. Sebagai permulaan, cara service worker ditetapkan dalam file manifes berbeda dengan cara penetapan skrip latar belakang. Selain itu:

  • Karena mereka tidak dapat mengakses DOM atau antarmuka window, Anda harus memindahkan panggilan tersebut ke API yang berbeda atau ke dalam dokumen di luar layar.
  • Pemroses peristiwa tidak boleh didaftarkan sebagai respons terhadap promise yang ditampilkan atau di dalam callback peristiwa.
  • Karena panggilan tersebut tidak kompatibel dengan XMLHttpRequest(), Anda harus mengganti panggilan ke antarmuka ini dengan panggilan ke fetch().
  • Karena aplikasi akan 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 "latar belakang" di manifes

Di Manifes V3, halaman latar belakang digantikan oleh service worker. Perubahan manifes tercantum di bawah ini.

  • Ganti "background.scripts" dengan "background.service_worker" di manifest.json. Perhatikan bahwa kolom "service_worker" menggunakan string, bukan array string.
  • Hapus "background.persistent" dari manifest.json.
Manifes V2
{
  ...
  "background": {
    "scripts": [
      "backgroundContextMenus.js",
      "backgroundOauth.js"
    ],
    "persistent": false
  },
  ...
}
Manifes V3
{
  ...
  "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 tersebut dengan membuka dan menutup dokumen yang belum 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 berinteraksi dengan ekstensi.

Untuk menggunakan Offscreen API, buat dokumen di luar layar dari pekerja layanan.

chrome.offscreen.createDocument({
  url: chrome.runtime.getURL('offscreen.html'),
  reasons: ['CLIPBOARD'],
  justification: 'testing the offscreen API',
});

Dalam dokumen offscreen, lakukan tindakan apa pun yang sebelumnya akan Anda jalankan di 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 penyampaian pesan.

Mengonversi localStorage ke jenis lain

Antarmuka Storage platform web (dapat diakses dari window.localStorage) tidak dapat digunakan dalam 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 lainnya tersedia.

Anda juga dapat memindahkan panggilannya ke dokumen di luar layar. Misalnya, untuk memigrasikan data yang sebelumnya disimpan di localStorage ke mekanisme lain:

  1. Buat dokumen di luar layar dengan rutinitas konversi dan pengendali runtime.onMessage.
  2. Tambahkan rutinitas konversi ke dokumen di luar layar.
  3. Di pekerja layanan ekstensi, periksa chrome.storage untuk mengetahui data Anda.
  4. Jika data Anda tidak ditemukan, buat dokumen di luar layar dan panggil runtime.sendMessage() untuk memulai rutinitas konversi.
  5. Di pengendali runtime.onMessage yang Anda tambahkan ke dokumen di luar layar, panggil rutinitas konversi.

Ada juga perbedaan mengenai cara kerja Web Storage API dalam 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);
});

Fungsi ini berfungsi pada halaman latar belakang yang 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 terlewatkan.

Sebagai gantinya, pindahkan pendaftaran pemroses peristiwa ke tingkat teratas skrip Anda. Hal ini memastikan bahwa Chrome dapat segera menemukan dan memanggil pengendali klik tindakan, 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 global fetch()

XMLHttpRequest() tidak dapat dipanggil dari pekerja layanan, ekstensi, atau lainnya. Ganti panggilan dari skrip latar belakang ke XMLHttpRequest() dengan panggilan ke fetch() global.

XMLHttpRequest()
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);
fetch()
const response = await fetch('https://www.example.com/greeting.json'')
console.log(response.statusText);

Mempertahankan status

Service worker bersifat sementara, artinya layanan kemungkinan akan memulai, menjalankan, dan menghentikan layanan berulang kali selama sesi browser pengguna. Ini juga berarti bahwa data tidak langsung tersedia dalam variabel global karena konteks sebelumnya telah dihapus. Untuk mengatasi hal ini, gunakan storage API sebagai sumber kebenaran. Contoh 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.

Skrip latar belakang Manifes V2
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.

Pekerja layanan Manifes V3
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 });
});

Konversi timer menjadi alarm

Menggunakan operasi tertunda atau berkala menggunakan metode setTimeout() atau setInterval() adalah hal yang umum. Namun, API ini bisa gagal di pekerja layanan, karena timer dibatalkan setiap kali pekerja layanan dihentikan.

Skrip latar belakang Manifes V2
// 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 terdaftar di level teratas skrip Anda.

Pekerja layanan Manifes V3
async function startAlarm(name, duration) {
  await chrome.alarms.create(name, { delayInMinutes: 3 });
}

chrome.alarms.onAlarm.addListener(() => {
  chrome.action.setIcon({
    path: getRandomIconPath(),
  });
});

Membuat pekerja layanan tetap aktif

Service worker menurut definisi didasarkan pada peristiwa dan akan dihentikan saat tidak aktif. 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 hidup dalam waktu yang lebih lama.

Membuat pekerja layanan tetap aktif hingga operasi yang berjalan lama selesai

Selama operasi pekerja layanan yang berjalan lama yang tidak memanggil API ekstensi, pekerja layanan mungkin dinonaktifkan di tengah operasi. Contohnya mencakup:

  • Permintaan fetch() berpotensi memerlukan waktu lebih dari lima menit (mis. download besar pada koneksi yang berpotensi buruk).
  • Penghitungan asinkron kompleks yang memerlukan waktu lebih dari 30 detik.

Untuk memperpanjang masa pakai pekerja layanan dalam kasus ini, Anda dapat secara berkala memanggil API ekstensi trivial untuk mereset penghitung waktu tunggu. Harap diperhatikan, ini hanya berlaku untuk kasus luar biasa dan dalam kebanyakan situasi, biasanya ada cara idiomatis platform yang lebih baik untuk mencapai hasil yang sama.

Contoh berikut menunjukkan fungsi bantuan waitUntil() yang membuat pekerja layanan tetap aktif hingga promise yang ditentukan diselesaikan:

async function waitUntil(promise) = {
  const keepAlive = setInterval(chrome.runtime.getPlatformInfo, 25 * 1000);
  try {
    await promise;
  } finally {
    clearInterval(keepAlive);
  }
}

waitUntil(someExpensiveCalculation());

Membuat pekerja layanan tetap hidup secara terus-menerus

Dalam kasus yang jarang terjadi, Anda perlu memperpanjang masa pakai tanpa batas waktu. Kami telah mengidentifikasi perusahaan dan pendidikan sebagai kasus penggunaan terbesar, dan kami secara khusus mengizinkan hal ini di sana, tetapi secara umum kami tidak mendukung hal ini. Dalam keadaan luar biasa ini, membuat pekerja layanan tetap aktif dapat dicapai dengan memanggil API ekstensi sederhana secara berkala. Perhatikan bahwa rekomendasi ini hanya berlaku bagi ekstensi yang berjalan di perangkat terkelola untuk kasus penggunaan perusahaan atau pendidikan. Tindakan tersebut tidak diizinkan dalam kasus lain dan tim ekstensi Chrome berhak mengambil tindakan terhadap ekstensi tersebut di masa mendatang.

Gunakan cuplikan kode berikut untuk membuat pekerja layanan Anda 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'];
}