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ını 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ı bağlamlarda "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 bunlara arka plan komut dosyası 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ına göre çeşitli farklılıklar gösterir.
- 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.
- İstemciler arayüzü üzerinden diğer bağlamlarla iletişim kurabilir ve etkileşimde bulunabilirler.
Yapmanız gereken değişiklikler
Arka plan komut dosyaları ile hizmet ç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
windowarayüzüne erişemedikleri için bu tür çağrıları farklı bir API'ye veya ekran dışı bir belgeye taşımanız gerekir. - Etkinlik dinleyicileri, 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ı sonlandırmak, zamanlayıcıları tamamlanmadan önce de durdurabilir. Bunları alarmlarla değiştirmeniz gerekir.
Bu sayfada bu görevler ayrıntılı olarak açıklanmaktadır.
Manifest'teki "background" alanını güncelleyin.
Manifest V3'te arka plan sayfalarının yerini hizmet çalışanı alıyor. Manifest değişiklikleri aşağıda listelenmiştir.
manifest.jsoniçinde"background.scripts"yerine"background.service_worker"koyun."service_worker"alanının dize dizisi değil, dize aldığını unutmayın."background.persistent",manifest.jsoncihazından kaldırılır.
{ ... "background": { "scripts": [ "backgroundContextMenus.js", "backgroundOauth.js" ], "persistent": false }, ... }
{ ... "background": { "service_worker": "service_worker.js", "type": "module" } ... }
"service_worker" alanı tek bir dize alır. ES modüllerini (import anahtar kelimesini kullanarak) kullanıyorsanız yalnızca "type" alanı gerekir. 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 paketlenmiş, gösterilmeyen belgeleri 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 ekran dışı bir doküman oluşturun.
browser.offscreen.createDocument({
url: browser.runtime.getURL('offscreen.html'),
reasons: ['CLIPBOARD'],
justification: 'testing the offscreen API',
});
Ekran dışı dokümanda, daha önce arka plan komut dosyasında çalıştırdığınız 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 iki yöntemden birini kullanın. İlk olarak, bunu başka bir depolama mekanizmasına yapılan çağrılarla değiştirebilirsiniz. browser.storage.local ad alanı çoğu kullanım alanına uygundur ancak diğer seçenekler de mevcuttur.
Ayrıca, aramaları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:
- Dönüşüm rutini ve
runtime.onMessageişleyicisi içeren bir ekran dışı doküman oluşturun. - Ekranda görünmeyen dokümana bir dönüştürme rutini ekleyin.
- Uzantı hizmeti çalışanı kontrolünde verileriniz için
browser.storagesimgesini bulun. - Verileriniz bulunamazsa ekran dışı bir doküman oluşturun ve dönüştürme rutinini başlatmak için
runtime.sendMessage()numaralı telefonu arayın. - Ekran dışı belgeye eklediğiniz
runtime.onMessageişleyicisinde dönüştürme rutinini çağırın.
Web depolama API'lerinin uzantılarda işleyiş şekliyle ilgili bazı nüanslar da vardır. Daha fazla bilgiyi Depolama ve Çerezler başlıklı makalede bulabilirsiniz.
Dinleyicileri 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.
browser.storage.local.get(["badgeText"], ({ badgeText }) => {
browser.browserAction.setBadgeText({ text: badgeText });
browser.browserAction.onClicked.addListener(handleActionClick);
});
Sayfa sürekli çalıştığı ve hiçbir zaman yeniden başlatılmadığı için bu işlem, 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 nedenle, 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.
browser.action.onClicked.addListener(handleActionClick);
browser.storage.local.get(["badgeText"], ({ badgeText }) => {
browser.action.setBadgeText({ text: badgeText });
});
XMLHttpRequest() işlevini genel fetch() işleviyle değiştirme
XMLHttpRequest(), bir hizmet çalışanı, uzantı veya başka bir şekilde aranamaz. Arka plan komut dosyanızdaki XMLHttpRequest() ile yapılan aramaları global fetch() ile yapılan aramalarla değiştirin.
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);
Durumları koruma
Service worker'lar geçicidir. 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 yıkıldığından verilerin genel değişkenlerde hemen kullanılamadığı anlamına da gelir. Bu sorunu çözmek için doğruluk kaynağı olarak depolama alanı API'lerini kullanın. Bunu nasıl yapacağınızı bir örnekle açıklayacağız.
Aşağıdaki örnekte, bir adı depolamak için genel değişken kullanılmaktadır. Bir hizmet çalışanında bu değişken, kullanıcının tarayıcı oturumu sırasında birden çok kez sıfırlanabilir.
let savedName = undefined; browser.runtime.onMessage.addListener(({ type, name }) => { if (type === "set-name") { savedName = name; } }); browser.browserAction.onClicked.addListener((tab) => { browser.tabs.sendMessage(tab.id, { name: savedName }); });
Manifest V3 için genel değişkeni Storage API çağrısıyla değiştirin.
browser.runtime.onMessage.addListener(({ type, name }) => { if (type === "set-name") { browser.storage.local.set({ name }); } }); browser.action.onClicked.addListener(async (tab) => { const { name } = await browser.storage.local.get(["name"]); browser.tabs.sendMessage(tab.id, { name }); });
Zamanlayıcıları alarma dönüştürme
setTimeout() veya setInterval() yöntemleri kullanılarak gecikmeli ya da periyodik işlemler yapılması 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.
// 3 minutes in milliseconds const TIMEOUT = 3 * 60 * 1000; setTimeout(() => { browser.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.
async function startAlarm(name, duration) { await browser.alarms.create(name, { delayInMinutes: 3 }); } browser.alarms.onAlarm.addListener(() => { browser.action.setIcon({ path: getRandomIconPath(), }); });
Hizmet çalışanını etkin tutma
Service worker'lar tanım gereği etkinliğe dayalı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ı istisnai durumlarda, hizmet çalışanının daha uzun süre çalışmaya devam etmesini sağlamak için ek önlemler gerekebilir.
Uzun süren 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 durumlarda kullanıldığını ve çoğu durumda aynı sonucu elde etmek için genellikle daha iyi ve platforma özgü bir yol 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(browser.runtime.getPlatformInfo, 25 * 1000);
try {
await promise;
} finally {
clearInterval(keepAlive);
}
}
waitUntil(someExpensiveCalculation());
Bir hizmet çalışanını sürekli olarak etkin tutma
Nadir durumlarda, kullanım ömrünün süresiz olarak uzatılması gerekir. En büyük kullanım alanları olarak kurumsal ve eğitim alanlarını belirledik. Bu alanlarda özellikle izin veriyoruz ancak genel olarak desteklemiyoruz. Bu istisnai durumlarda, 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ısı 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ı etkin tutun:
/**
* 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 browser.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 browser.storage.local.get('last-heartbeat'))['last-heartbeat'];
}