Service Worker'a geçin

Arka planı veya etkinlik sayfalarını bir hizmet çalışanıyla değiştirme

Bir hizmet çalışanı, arka plan kodunun ana iş parçacığından uzak kalmasını sağlamak için uzantının arka plan veya etkinlik sayfasını değiştirir. Bu sayede uzantılar yalnızca gerektiğinde çalışır ve kaynak tasarrufu sağlanır.

Arka plan sayfaları, kullanıma sunulduklarından beri uzantıların temel bir bileşeni olmuştur. Basitçe ifade etmek gerekirse arka plan sayfaları, diğer pencerelerden veya sekmelerden bağımsız bir ortam sağlar. Bu sayede uzantılar, etkinlikleri gözlemleyebilir ve etkinliklere yanıt olarak işlem yapabilir.

Bu sayfada, arka plan sayfalarını uzantı hizmeti çalışanlarına dönüştürme görevleri açıklanmaktadır. Genel olarak uzantı hizmeti çalışanları hakkında daha fazla bilgi için Hizmet çalışanlarıyla etkinlikleri işleme başlıklı eğitime ve Uzantı hizmeti çalışanları hakkında bölümüne bakın.

Arka plan komut dosyaları ile uzantı hizmeti çalışanları arasındaki farklar

Bazı durumlarda "arka plan komut dosyaları" olarak adlandırılan uzantı hizmeti çalışanlarını görürsünüz. Uzantı hizmeti çalışanları arka planda çalışsa da onlara arka plan komut dosyaları demek, aynı özelliklere sahip oldukları anlamına geldiği için yanıltıcı olabilir. Farklar aşağıda açıklanmıştır.

Arka plan sayfalarındaki değişiklikler

Service worker'lar, arka plan sayfalarından farklıdır.

  • Ana iş parçacığı dışında çalıştıkları için uzantı içeriğine müdahale etmezler.
  • Uzantının kaynağındaki getirme etkinliklerini (ör. araç çubuğu pop-up'ından gelenler) yakalama gibi özel yeteneklere sahiptirler.
  • Clients arayüzü üzerinden diğer bağlamlarla iletişim kurabilir ve etkileşimde bulunabilirler.

Yapmanız gereken değişiklikler

Arka plan komut dosyaları ve servis çalışanlarının işleyiş şekli arasındaki farklılıkları hesaba katmak için kodda birkaç düzenleme yapmanız gerekir. Öncelikle, hizmet çalışanının manifest dosyasında belirtilme şekli, arka plan komut dosyalarının belirtilme şeklinden farklıdır. Ayrıca:

  • DOM'a veya window arayüzüne erişemedikleri için bu tür çağrıları farklı bir API'ye veya ekran dışı bir dokümana taşımanız gerekir.
  • Etkinlik işleyiciler, döndürülen sözlere yanıt olarak veya etkinlik geri çağırmalarının içinde kaydedilmemelidir.
  • XMLHttpRequest() ile geriye dönük uyumlu olmadıkları için bu arayüze yapılan çağrıları fetch() çağrılarıyla değiştirmeniz gerekir.
  • Kullanılmadıklarında sonlandırıldıkları için global değişkenlere güvenmek yerine uygulama durumlarını kalıcı hale getirmeniz gerekir. Hizmet çalışanlarının sonlandırılması, zamanlayıcıların tamamlanmadan sona ermesine de neden olabilir. Bunları alarmlarla değiştirmeniz gerekir.

Bu sayfada, bu görevler ayrıntılı olarak açıklanmaktadır.

Manifest dosyasındaki "background" alanını güncelleyin.

Manifest V3'te arka plan sayfalarının yerini hizmet çalışanı alır. Manifest değişiklikleri aşağıda listelenmiştir.

  • manifest.json içinde "background.scripts" öğesini "background.service_worker" ile değiştirin. "service_worker" alanının dize dizisi değil, dize aldığını unutmayın.
  • "background.persistent" öğesini manifest.json öğesinden kaldırın.
Manifest V2
{
  ...
  "background": {
    "scripts": [
      "backgroundContextMenus.js",
      "backgroundOauth.js"
    ],
    "persistent": false
  },
  ...
}
Manifest V3
{
  ...
  "background": {
    "service_worker": "service_worker.js",
    "type": "module"
  }
  ...
}

"service_worker" alanı tek bir dize alır. Yalnızca ES modüllerini (import anahtar kelimesini kullanarak) kullanıyorsanız "type" alanına ihtiyacınız olur. Bu alanın değeri her zaman "module" olur. Daha fazla bilgi için Uzantı hizmeti çalışanıyla ilgili temel bilgiler başlıklı makaleyi inceleyin.

DOM ve pencere çağrılarını ekran dışı bir dokümana taşıma

Bazı uzantıların, yeni bir pencere veya sekme açmadan DOM ve pencere nesnelerine erişmesi gerekir. Offscreen API, kullanıcı deneyimini kesintiye uğratmadan uzantıyla birlikte paketlenmiş ve gösterilmeyen dokümanları açıp kapatarak bu kullanım alanlarını destekler. Ekran dışı dokümanlar, mesaj iletme dışında diğer uzantı bağlamlarıyla API paylaşmaz ancak uzantıların etkileşim kurabileceği tam web sayfaları olarak işlev görür.

Offscreen API'yi kullanmak için hizmet çalışanından bir offscreen dokümanı oluşturun.

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

Ekran dışı belgede, daha önce arka plan komut dosyasında çalıştırdığınız tüm işlemleri gerçekleştirin. Örneğin, ana makine sayfasında seçilen metni kopyalayabilirsiniz.

let textEl = document.querySelector('#text');
textEl.value = data;
textEl.select();
document.execCommand('copy');

Mesaj iletme özelliğini kullanarak ekran dışı belgeler ve uzantı hizmeti çalışanları arasında iletişim kurun.

localStorage'ı başka bir türe dönüştürme

Web platformunun Storage arayüzü (window.localStorage adresinden erişilebilir) bir hizmet çalışanında kullanılamaz. Bu sorunu gidermek için aşağıdaki iki yöntemden birini kullanın. İlk olarak, bunu başka bir depolama mekanizmasına yapılan çağrılarla değiştirebilirsiniz. chrome.storage.local ad alanı çoğu kullanım alanına uygundur ancak diğer seçenekler de mevcuttur.

Ayrıca, çağrılarını ekran dışı bir dokümana da taşıyabilirsiniz. Örneğin, daha önce localStorage içinde depolanan verileri başka bir mekanizmaya taşımak için:

  1. Dönüşüm rutini ve runtime.onMessage işleyicisi içeren bir ekran dışı doküman oluşturun.
  2. Ekranda görünmeyen dokümana bir dönüştürme rutini ekleyin.
  3. Verileriniz için uzantı hizmeti çalışanı kontrolünde chrome.storage simgesini bulun.
  4. Verileriniz bulunamazsa ekran dışı bir doküman oluşturun ve dönüştürme rutinini başlatmak için runtime.sendMessage()'ı arayın.
  5. Ekran dışı belgeye eklediğiniz runtime.onMessage işleyicisinde dönüşüm rutinini çağırın.

Web depolama API'lerinin uzantılarda çalışma şekliyle ilgili bazı nüanslar da vardır. Daha fazla bilgiyi Depolama ve Çerezler başlıklı makalede bulabilirsiniz.

İşleyicileri eşzamanlı olarak kaydetme

Bir dinleyiciyi eşzamansız olarak (ör. bir söz veya geri çağırma içinde) kaydetmenin Manifest V3'te çalışacağı garanti edilmez. Aşağıdaki kodu inceleyin.

chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
  chrome.browserAction.setBadgeText({ text: badgeText });
  chrome.browserAction.onClicked.addListener(handleActionClick);
});

Sayfa sürekli çalıştığı ve hiçbir zaman yeniden başlatılmadığı için bu, kalıcı bir arka plan sayfasıyla çalışır. Manifest V3'te, etkinlik gönderildiğinde hizmet çalışanı yeniden başlatılır. Bu durumda, etkinlik tetiklendiğinde işleyiciler kaydedilmez (eşzamansız olarak eklendikleri için) ve etkinlik kaçırılır.

Bunun yerine, etkinlik işleyici kaydını komut dosyanızın en üst düzeyine taşıyın. Bu sayede, uzantınız başlangıç mantığını yürütmeyi tamamlamamış olsa bile Chrome, işleminizin tıklama işleyicisini anında bulup çağırabilir.

chrome.action.onClicked.addListener(handleActionClick);

chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
  chrome.action.setBadgeText({ text: badgeText });
});

XMLHttpRequest() işlevini genel fetch() işleviyle değiştirme

XMLHttpRequest(), bir hizmet çalışanı, uzantı veya başka bir yerden çağrılamaz. Arka plan komut dosyanızdaki XMLHttpRequest() ile yapılan aramaları global fetch() ile yapılan aramalarla değiştirin.

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);

Durumları kalıcı hale getirme

Service worker'lar kısa ömürlüdür. Bu nedenle, kullanıcının tarayıcı oturumu sırasında tekrar tekrar başlatılır, çalıştırılır ve sonlandırılır. Ayrıca, önceki bağlam kaldırıldığından verilerin genel değişkenlerde hemen kullanılamadığı anlamına da gelir. Bu sorunu çözmek için bilgi kaynağı olarak depolama API'lerini kullanın. Bunun nasıl yapılacağını bir örnekle açıklayacağız.

Aşağıdaki örnekte, bir adı depolamak için genel bir değişken kullanılmaktadır. Bir hizmet çalışanında bu değişken, kullanıcının tarayıcı oturumu boyunca birden çok kez sıfırlanabilir.

Manifest V2 arka plan komut dosyası
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 });
});

Manifest V3 için genel değişkeni Storage API çağrısıyla değiştirin.

Manifest V3 hizmet çalışanı
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 });
});

Zamanlayıcıları alarma dönüştürme

setTimeout() veya setInterval() yöntemlerini kullanarak gecikmeli ya da periyodik işlemler yapmak yaygındır. Ancak hizmet çalışanı sonlandırıldığında zamanlayıcılar iptal edildiğinden bu API'ler hizmet çalışanlarında başarısız olabilir.

Manifest V2 arka plan komut dosyası
// 3 minutes in milliseconds
const TIMEOUT = 3 * 60 * 1000;
setTimeout(() => {
  chrome.action.setIcon({
    path: getRandomIconPath(),
  });
}, TIMEOUT);

Bunun yerine Alarms API'yi kullanın. Diğer dinleyicilerde olduğu gibi, alarm dinleyicileri de komut dosyanızın en üst düzeyinde kaydedilmelidir.

Manifest V3 hizmet çalışanı
async function startAlarm(name, duration) {
  await chrome.alarms.create(name, { delayInMinutes: 3 });
}

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

Hizmet çalışanını etkin tutma

Hizmet çalışanları tanım gereği olay odaklıdır ve işlem yapılmadığında sonlandırılır. Bu sayede Chrome, uzantınızın performansını ve bellek tüketimini optimize edebilir. Daha fazla bilgiyi hizmet çalışanı yaşam döngüsü dokümanımızda bulabilirsiniz. Bazı durumlarda, hizmet çalışanının daha uzun süre etkin kalmasını sağlamak için ek önlemler gerekebilir.

Uzun süreli bir işlem tamamlanana kadar hizmet çalışanını etkin tutma

Uzantı API'lerini çağırmayan uzun süreli hizmet çalışanı işlemleri sırasında hizmet çalışanı, işlem ortasında kapatılabilir. Örnekler:

  • Beş dakikadan uzun sürebilecek bir fetch() isteği (ör. bağlantı kalitesi düşük olabilecek bir bağlantıda büyük bir indirme işlemi).
  • 30 saniyeden uzun süren karmaşık bir eşzamansız hesaplama.

Bu durumlarda hizmet çalışanı ömrünü uzatmak için zaman aşımı sayacını sıfırlamak üzere önemsiz bir uzantı API'sini düzenli olarak çağırabilirsiniz. Bu yöntemin yalnızca istisnai durumlar için ayrıldığını ve çoğu durumda aynı sonucu elde etmenin daha iyi ve platforma özgü bir yolu olduğunu lütfen unutmayın.

Aşağıdaki örnekte, belirli bir söz çözümlenene kadar hizmet çalışanınızı etkin tutan bir waitUntil() yardımcı işlevi gösterilmektedir:

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

waitUntil(someExpensiveCalculation());

Bir hizmet çalışanını sürekli olarak etkin tutma

Nadiren de olsa, kullanım ömrünün süresiz olarak uzatılması gerekebilir. En büyük kullanım alanları olarak kurumsal ve eğitim alanlarını belirledik. Bu özelliği özellikle bu alanlarda kullanıma sunuyoruz ancak genel olarak desteklemiyoruz. Bu istisnai durumlarda, bir hizmet çalışanını etkin tutmak için önemsiz bir uzantı API'si periyodik olarak çağrılabilir. Bu önerinin yalnızca kurumsal veya eğitim amaçlı kullanım alanlarında yönetilen cihazlarda çalışan uzantılar için geçerli olduğunu unutmayın. Diğer durumlarda buna izin verilmez ve Chrome uzantı ekibi, gelecekte bu uzantılara karşı işlem yapma hakkını saklı tutar.

Aşağıdaki kod snippet'ini kullanarak hizmet çalışanınızın etkin kalmasını sağlayın:

/**
 * 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'];
}