Service Worker'a geçin

Arka plan veya etkinlik sayfalarını Service Worker ile değiştirme

Hizmet çalışanları, arka plan kodunun ana iş parçacığında kalmamasını sağlamak için uzantının arka planını veya etkinlik sayfasını değiştirir. Bu sayede uzantılar yalnızca gerektiğinde çalışır ve kaynaklardan tasarruf edilir.

Arka plan sayfaları, kullanıma sunuldukları günden bu yana uzantıların temel bileşenlerinden biri olmuştur. Basitçe söylemek gerekirse, arka plan sayfaları diğer pencere veya sekmelerden bağımsız bir ortam sağlar. Bu sayede uzantılar, etkinlikleri gözlemleyebilir ve bunlara yanıt verebilir.

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

Arka plan komut dosyaları ile uzantı hizmet işçileri arasındaki farklar

Bazı bağlamlarda "arka plan komut dosyaları" olarak adlandırılan uzantı hizmet işçilerini görürsünüz. Uzantı hizmet çalışanları arka planda çalışsa da arka plan komut dosyaları olarak adlandırılmaları, aynı özelliklere sahip olduklarını ima ederek biraz yanıltıcıdır. Farklar aşağıda açıklanmıştır.

Arka plan sayfalarındaki değişiklikler

Hizmet çalışanları, arka plan sayfalarıyla bazı farklılıklara sahiptir.

  • Ana mesaj dizisinden bağımsız olarak çalışırlar. Bu nedenle, uzantı içeriğini etkilemezler.
  • Uzantı kaynağındaki getirme etkinliklerini (ör. araç çubuğu pop-up'ındakiler) durdurma gibi özel yetenekleri vardır.
  • İstemciler arayüzü aracılığıyla diğer bağlamlarla iletişim kurabilir ve etkileşimde bulunabilirler.

Yapmanız gereken değişiklikler

Arka plan komut dosyalarının ve Service Worker'ların çalışma biçimleri arasındaki farkları hesaba katmak için kodda birkaç düzenleme yapmanız gerekir. Öncelikle, bir 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 dinleyicileri, döndürülen vaatlere yanıt olarak veya etkinlik geri çağırmalarının içine kaydedilmemelidir.
  • Bu API'ler XMLHttpRequest() ile geriye dönük uyumlu olmadığından bu arayüze yapılan çağrıları fetch() arayüzüne yapılan çağrılarla değiştirmeniz gerekir.
  • Kullanılmadığında sonlandırıldıkları için global değişkenlere güvenmek yerine uygulama durumlarını korumanız gerekir. Hizmet işçileri sonlandırılırsa zamanlayıcılar da tamamlanmadan sonlandırılabilir. Bunları alarmlarla değiştirmeniz gerekir.

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

Manifest dosyasında "background" alanını güncelleyin

Manifest V3'te, arka plan sayfaları bir hizmet çalışanı ile değiştirilir. Manifest değişiklikleri aşağıda listelenmiştir.

  • manifest.json içinde "background.scripts" değerini "background.service_worker" ile değiştirin. "service_worker" alanının bir dize dizisi değil, dize aldığını unutmayın.
  • "background.persistent", manifest.json hesabından kaldırılsın mı?
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. "type" alanına yalnızca ES modülleri (import anahtar kelimesini kullanarak) kullanıyorsanız ihtiyacınız vardır. Değeri her zaman "module" olur. Daha fazla bilgi için Uzantı hizmet işçisi ile 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'e ve pencere nesnelerine erişmesi gerekir. Outscreen API, kullanıcı deneyimini etkilemeden, uzantıyla paketlenmiş görüntülenmemiş dokümanları açıp kapatarak bu kullanım alanlarını destekler. Ekran dışı belgeler, mesaj aktarımı dışında API'leri diğer uzantı bağlamlarıyla paylaşmaz ancak uzantıların etkileşime geçebileceği tam web sayfaları olarak işlev görür.

Ekran Dışı API'yi kullanmak için hizmet işçisinden ekran dışı bir doküman oluşturun.

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

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

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

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

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

Web platformunun Storage arayüzü (window.localStorage üzerinden erişilebilir) bir hizmet çalışanında kullanılamaz. Bu sorunu gidermek için iki seçenekten birini uygulayı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, bu işlevin ç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şleyici ile ekran dışı bir doküman oluşturun.
  2. Ekran dışındaki dokümana dönüşüm rutini ekleyin.
  3. Uzantı hizmet işçisinde verileriniz için chrome.storage öğesini kontrol edin.
  4. Verileriniz bulunamazsa ekran dışı bir doküman create ve dönüşüm 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 de bazı ince ayrıntılar vardır. Daha fazla bilgi için Depolama alanı ve çerezler başlıklı makaleyi inceleyin.

İşleyicileri eşzamanlı olarak kaydetme

Bir dinleyicinin asenkron olarak (ör. bir promise veya geri çağırma içinde) kaydedilmesinin Manifest V3'te çalışacağı garanti edilmez. Aşağıdaki kodu ele alalım.

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

Bu yöntem, sayfa sürekli çalıştığı ve hiçbir zaman yeniden başlatılmadığı için kalıcı arka plan sayfalarında işe yarar. Manifest V3'te, etkinlik gönderildiğinde hizmet çalışanı yeniden başlatılır. Bu, etkinlik tetiklendiğinde işleyicilerin kaydedilmeyeceği (asynchronize olarak eklendikleri için) ve etkinliğin kaçıracağı anlamına gelir.

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

chrome.action.onClicked.addListener(handleActionClick);

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

XMLHttpRequest() işlevini genel getirme() ile değiştirin

XMLHttpRequest(), bir hizmet çalışanından, uzantıdan veya başka bir yerden çağrılamaz. Arka plan komut dosyanızdan XMLHttpRequest() öğesine yapılan çağrıları genel fetch() çağrılarıyla 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ı koruma

Hizmet çalışanları geçicidir. Bu, kullanıcının tarayıcı oturumu sırasında muhtemelen tekrar tekrar başlatılacakları, çalıştırılacakları ve sonlandırılacakları anlamına gelir. Ayrıca, önceki bağlam kaldırıldığı için verilerin genel değişkenlerde hemen kullanılamaması anlamına da gelir. Bu sorunu önlemek için bilgi kaynağı olarak depolama API'lerini kullanın. Aşağıda bunun nasıl yapılacağı gösterilmektedir.

Aşağıdaki örnekte ad depolamak için genel değişken kullanılmaktadır. Bir Service Worker'da 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, bir 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ı alarmlara dönüştürme

setTimeout() veya setInterval() yöntemlerini kullanarak gecikmeli veya periyodik işlemler kullanmak yaygındır. Ancak zamanlayıcılar, hizmet çalışanı sonlandırıldığında iptal edildiği için 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 senaryonuzun en üst düzeyine 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(),
  });
});

Service Worker'ı aktif tutun

Service Worker'lar tanımı gereği etkinliğe dayalıdır ve herhangi bir işlem yapmadıklarında feshedilir. Bu sayede Chrome, uzantınızın performansını ve bellek tüketimini optimize edebilir. Daha fazla bilgi için Service Worker yaşam döngüsü belgemizi inceleyin. İstisnai durumlarda, hizmet işçisinin daha uzun süre etkin kalması için ek önlemler gerekebilir.

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

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

  • Beş dakikadan uzun sürebilecek bir fetch() isteği (ör. zayıf bağlantıda büyük bir indirme).
  • 30 saniyeden uzun süren karmaşık bir asenkron hesaplama.

Bu gibi durumlarda hizmet çalışanı ömrünü uzatmak için zaman aşımı sayacını sıfırlamak üzere periyodik olarak basit bir uzantı API'si çağırabilirsiniz. Bunun yalnızca istisnai durumlar için ayrıldığını ve çoğu durumda aynı sonuca ulaşmanın genellikle daha iyi ve platforma özgü bir yolu olduğunu unutmayın.

Aşağıdaki örnekte, belirli bir taahhüt çözülene kadar hizmet çalışanınızı aktif durumda tutacak 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());

Hizmet işçisini sürekli olarak etkin tutma

Nadir durumlarda, kullanım ömrünü süresiz olarak uzatmak gerekir. En büyük kullanım alanları olarak kurumsal ve eğitim alanlarını belirledik ve bu alanlarda özellikle buna izin veriyoruz ancak genel olarak bu özelliği desteklemiyoruz. Bu istisnai durumlarda, basit bir uzantı API'si periyodik olarak çağrılarak hizmet çalışanının etkin kalması sağlanabilir. Bu önerinin yalnızca kurumsal veya eğitim kullanım alanları için yönetilen cihazlarda çalışan uzantılar için geçerli olduğunu unutmayın. Diğer durumlarda izin verilmez ve Chrome uzantı ekibi, gelecekte bu uzantılara karşı işlem yapma hakkını saklı tutar.

Hizmet çalışanınızın etkin kalmasını sağlamak için aşağıdaki kod snippet'ini kullanı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'];
}