Arka plan veya etkinlik sayfalarını hizmet çalışanıyla 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. Farklılıklar 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ü üzerinden diğer bağlamlarla iletişim kurabilir ve etkileşime geçebilirler.
Yapmanız gereken değişiklikler
Arka plan komut dosyalarının ve servis çalışanlarının işleyiş şekli arasındaki farkları hesaba katmak için birkaç kod düzenlemesi 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ıyla değiştirilir. Manifest değişiklikleri aşağıda listelenmiştir.
manifest.json
içinde"background.scripts"
yerine"background.service_worker"
yazın."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ı?
{ ... "background": { "scripts": [ "backgroundContextMenus.js", "backgroundOauth.js" ], "persistent": false }, ... }
{ ... "background": { "service_worker": "service_worker.js", "type": "module" } ... }
"service_worker"
alanı tek bir dize alır. "type"
alanına yalnızca ES modüllerini (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. Offscreen API, kullanıcı deneyimini kesintiye uğratmadan uzantı ile paketlenmiş görüntülenmeyen belgeleri 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 aktarma özelliğini kullanarak ekran dışındaki belgeler ile uzantı hizmet ç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) hizmet çalışanında kullanılamaz. Bu sorunu gidermek için iki seçenekten birini uygulayın. Öncelikle, bu işlevi başka bir depolama mekanizmasına yönelik ç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:
- Dönüşüm rutini ve
runtime.onMessage
işleyici içeren bir ekran dışı doküman oluşturun. - Ekran dışı dokümana bir dönüşüm rutini ekleyin.
- Uzantı hizmet işçisinde verileriniz için
chrome.storage
öğesini kontrol edin. - Verileriniz bulunamazsa ekran dışı bir doküman create ve dönüşüm rutinini başlatmak için
runtime.sendMessage()
'ı arayın. - Ekran dışı belgeye eklediğiniz
runtime.onMessage
işleyicisinde dönüşüm rutinini çağırın.
Web depolama alanı API'lerinin uzantılarda işleyiş şekliyle ilgili bazı nüanslar da 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 dinleyici kaydını komut dosyanızın ü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 global fetch() işleviyle değiştirin
XMLHttpRequest()
, hizmet çalışanı, uzantı veya başka bir yerden çağrılamaz. Arka plan komut dosyanızdaki XMLHttpRequest()
çağrılarını global fetch()
çağrılarıyla 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
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ı da bu duruma yol açar. Bu sorunun üstesinden gelmek için bilgi kaynağı olarak depolama API'lerini kullanın. Aşağıda bunun nasıl yapılacağına dair bir örnek verilmiştir.
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.
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.
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.
// 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 dinleyiciler gibi alarm dinleyicileri de komut dosyanızın üst düzeyine kaydedilmelidir.
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 durumda tutma
Hizmet çalışanları, tanımı gereği olay odaklı olup işlem yapılmadığında sonlandırılır. Bu sayede Chrome, uzantınızın performansını ve bellek tüketimini optimize edebilir. Hizmet çalışanı yaşam döngüsü dokümanlarımızda daha fazla bilgi edinebilirsiniz. İstisnai durumlarda, hizmet işçisinin daha uzun süre etkin kalmasını sağlamak 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 asynkron 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 söz çözülene kadar hizmet işleyicinizi etkin durumda 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());
Hizmet işçisini sürekli olarak etkin tutma
Nadir durumlarda, kullanım süresinin süresiz olarak uzatılması 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 işleyicinizi etkin durumda tutmak 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'];
}