Achtergrond- of gebeurtenispagina's vervangen door een servicemedewerker
Een service worker vervangt de achtergrond- of gebeurtenispagina van de extensie om ervoor te zorgen dat achtergrondcode niet op de hoofdthread wordt uitgevoerd. Hierdoor worden extensies alleen uitgevoerd wanneer dat nodig is, wat resources bespaart.
Achtergrondpagina's zijn sinds hun introductie een fundamenteel onderdeel van extensies. Simpel gezegd bieden achtergrondpagina's een omgeving die onafhankelijk is van elk ander venster of tabblad. Hierdoor kunnen extensies gebeurtenissen waarnemen en erop reageren.
Deze pagina beschrijft taken voor het converteren van achtergrondpagina's naar extension service workers. Voor meer informatie over extension service workers in het algemeen, zie de handleiding ' Gebeurtenissen afhandelen met service workers' en het gedeelte 'Over extension service workers' .
Verschillen tussen achtergrondscripts en extension service workers
In sommige contexten worden extension service workers ook wel 'achtergrondscripts' genoemd. Hoewel extension service workers inderdaad op de achtergrond draaien, is de benaming 'achtergrondscripts' enigszins misleidend omdat het suggereert dat ze dezelfde mogelijkheden hebben. De verschillen worden hieronder beschreven.
Wijzigingen ten opzichte van achtergrondpagina's
Er zijn een aantal verschillen tussen servicemedewerkers en achtergrondpagina's.
- Ze functioneren buiten de hoofdthread, wat betekent dat ze geen invloed hebben op de inhoud van extensies.
- Ze beschikken over speciale mogelijkheden, zoals het onderscheppen van fetch-gebeurtenissen op de oorsprong van de extensie, bijvoorbeeld die van een pop-upvenster in de werkbalk.
- Ze kunnen via de clientinterface communiceren en interageren met andere contexten.
Wijzigingen die u moet aanbrengen
Je moet een paar codeaanpassingen maken om rekening te houden met de verschillen in de werking van achtergrondscripts en service workers. Om te beginnen wordt een service worker anders gespecificeerd in het manifestbestand dan een achtergrondscript. Daarnaast geldt het volgende:
- Omdat ze geen toegang hebben tot de DOM of de
window, moet je dergelijke aanroepen verplaatsen naar een andere API of naar een offscreen-document. - Eventlisteners mogen niet worden geregistreerd als reactie op geretourneerde promises of binnen eventcallbacks.
- Omdat ze niet achterwaarts compatibel zijn met
XMLHttpRequest()moet u aanroepen naar deze interface vervangen door aanroepen naarfetch(). - Omdat ze stoppen wanneer ze niet in gebruik zijn, moet u de applicatiestatus opslaan in plaats van te vertrouwen op globale variabelen. Het stoppen van service workers kan er ook voor zorgen dat timers voortijdig worden beëindigd. U moet deze vervangen door alarmen.
Op deze pagina worden deze taken in detail beschreven.
Werk het veld "achtergrond" in het manifest bij.
In Manifest V3 worden achtergrondpagina's vervangen door een service worker . De wijzigingen in het manifest staan hieronder vermeld.
- Vervang
"background.scripts"door"background.service_worker"in hetmanifest.json. Let op: het veld"service_worker"accepteert een tekenreeks, geen array van tekenreeksen. - Verwijder
"background.persistent"uit hetmanifest.json.
{ ... "background": { "scripts": [ "backgroundContextMenus.js", "backgroundOauth.js" ], "persistent": false }, ... }
{ ... "background": { "service_worker": "service_worker.js", "type": "module" } ... }
Het veld "service_worker" accepteert één enkele tekenreeks. Het veld "type" is alleen nodig als u ES-modules gebruikt (met het trefwoord import ). De waarde ervan is altijd "module" . Zie voor meer informatie de basisprincipes van de Extension Service Worker.
Verplaats DOM- en window-aanroepen naar een offscreen-document.
Sommige extensies hebben toegang nodig tot de DOM en vensterobjecten zonder dat er een nieuw venster of tabblad visueel wordt geopend. De Offscreen API ondersteunt deze gebruikssituaties door niet-weergegeven documenten die met de extensie zijn meegeleverd te openen en te sluiten, zonder de gebruikerservaring te verstoren. Behalve voor het doorgeven van berichten delen offscreen-documenten geen API's met andere extensiecontexten, maar functioneren ze als volwaardige webpagina's waarmee extensies kunnen interageren.
Om de Offscreen API te gebruiken, moet u een offscreen-document aanmaken vanuit de service worker.
chrome.offscreen.createDocument({
url: chrome.runtime.getURL('offscreen.html'),
reasons: ['CLIPBOARD'],
justification: 'testing the offscreen API',
});
Voer in het offscreen-document alle acties uit die u voorheen in een achtergrondscript zou hebben uitgevoerd. U kunt bijvoorbeeld geselecteerde tekst op de hostpagina kopiëren.
let textEl = document.querySelector('#text');
textEl.value = data;
textEl.select();
document.execCommand('copy');
Communiceer tussen documenten buiten het scherm en medewerkers van de extensieservice met behulp van berichtuitwisseling .
Converteer localStorage naar een ander type.
De Storage interface van het webplatform (toegankelijk via window.localStorage ) kan niet worden gebruikt in een service worker. Om dit op te lossen, kunt u een van de volgende twee dingen doen. Ten eerste kunt u deze vervangen door aanroepen naar een ander opslagmechanisme. De namespace chrome.storage.local is geschikt voor de meeste gebruikssituaties, maar er zijn ook andere opties beschikbaar.
Je kunt de aanroepen ook verplaatsen naar een document dat niet op het scherm wordt weergegeven . Bijvoorbeeld om gegevens die voorheen in localStorage waren opgeslagen, naar een ander mechanisme te migreren:
- Maak een offscreen-document aan met een conversieroutine en een
runtime.onMessagehandler. - Voeg een conversieroutine toe aan het document dat niet op het scherm wordt weergegeven.
- Controleer in de service worker van de extensie de map
chrome.storagevoor uw gegevens. - Als uw gegevens niet worden gevonden, maak dan een offscreen-document aan en roep
runtime.sendMessage()aan om de conversieroutine te starten. - Roep in de
runtime.onMessage-handler die u aan het offscreen-document hebt toegevoegd, de conversieroutine aan.
Er zijn ook enkele nuances met betrekking tot de werking van webopslag-API's in extensies. Lees meer in Opslag en cookies .
Registreer luisteraars synchroon
Het asynchroon registreren van een listener (bijvoorbeeld binnen een promise of callback) werkt niet gegarandeerd in Manifest V3. Bekijk de volgende code.
chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
chrome.browserAction.setBadgeText({ text: badgeText });
chrome.browserAction.onClicked.addListener(handleActionClick);
});
Dit werkt met een persistente achtergrondpagina omdat de pagina constant actief is en nooit opnieuw wordt geïnitialiseerd. In Manifest V3 wordt de service worker opnieuw geïnitialiseerd wanneer de gebeurtenis wordt verzonden. Dit betekent dat wanneer de gebeurtenis plaatsvindt, de listeners niet geregistreerd zijn (omdat ze asynchroon worden toegevoegd) en de gebeurtenis wordt gemist.
Verplaats de registratie van de gebeurtenislistener in plaats daarvan naar het hoogste niveau van uw script. Dit zorgt ervoor dat Chrome de klikhandler van uw actie direct kan vinden en aanroepen, zelfs als uw extensie de opstartlogica nog niet volledig heeft uitgevoerd.
chrome.action.onClicked.addListener(handleActionClick);
chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
chrome.action.setBadgeText({ text: badgeText });
});
Vervang XMLHttpRequest() door globale fetch().
XMLHttpRequest() kan niet worden aangeroepen vanuit een service worker, extensie of anderszins. Vervang aanroepen van XMLHttpRequest() vanuit uw achtergrondscript door aanroepen van de globale fetch() .
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);
Volhardende staten
Service workers zijn tijdelijk, wat betekent dat ze waarschijnlijk herhaaldelijk starten, draaien en stoppen tijdens een browsersessie. Dit betekent ook dat gegevens niet direct beschikbaar zijn in globale variabelen, omdat de vorige context is afgesloten. Om dit te omzeilen, kunt u opslag-API's gebruiken als de bron van waarheid. Een voorbeeld zal laten zien hoe dit werkt.
Het volgende voorbeeld gebruikt een globale variabele om een naam op te slaan. In een service worker kan deze variabele meerdere keren worden gereset gedurende de browsersessie van een gebruiker.
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 }); });
Voor Manifest V3 vervangt u de globale variabele door een aanroep naar de Storage API .
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 }); });
Zet timers om in alarmen.
Het is gebruikelijk om vertraagde of periodieke bewerkingen te gebruiken met de methoden setTimeout() of setInterval() . Deze API's kunnen echter problemen veroorzaken in service workers, omdat de timers worden geannuleerd zodra de service worker wordt beëindigd.
// 3 minutes in milliseconds const TIMEOUT = 3 * 60 * 1000; setTimeout(() => { chrome.action.setIcon({ path: getRandomIconPath(), }); }, TIMEOUT);
Gebruik in plaats daarvan de Alarms API . Net als andere listeners moeten alarmlisteners op het hoogste niveau van uw script worden geregistreerd.
async function startAlarm(name, duration) { await chrome.alarms.create(name, { delayInMinutes: 3 }); } chrome.alarms.onAlarm.addListener(() => { chrome.action.setIcon({ path: getRandomIconPath(), }); });
Zorg dat de hulpverlener in leven blijft.
Service workers zijn per definitie gebeurtenisgestuurd en worden beëindigd bij inactiviteit. Op deze manier kan Chrome de prestaties en het geheugenverbruik van uw extensie optimaliseren. Lees meer in onze documentatie over de levenscyclus van service workers . In uitzonderlijke gevallen kunnen aanvullende maatregelen nodig zijn om ervoor te zorgen dat een service worker langer actief blijft.
Houd een servicemedewerker in leven totdat een langdurige operatie is afgerond.
Tijdens langdurige service worker-bewerkingen die geen extensie-API's aanroepen, kan de service worker halverwege de bewerking worden afgesloten. Voorbeelden hiervan zijn:
- Een
fetch()-verzoek dat mogelijk langer dan vijf minuten duurt (bijvoorbeeld een grote download op een mogelijk slechte verbinding). - Een complexe asynchrone berekening die langer dan 30 seconden duurt.
Om de levensduur van de service worker in deze gevallen te verlengen, kunt u periodiek een eenvoudige extensie-API aanroepen om de time-outteller te resetten. Houd er rekening mee dat dit alleen voor uitzonderlijke gevallen is bedoeld en dat er in de meeste situaties een betere, platformspecifieke manier is om hetzelfde resultaat te bereiken.
Het volgende voorbeeld laat een waitUntil() hulpfunctie zien die uw service worker actief houdt totdat een bepaalde promise is opgelost:
async function waitUntil(promise) {
const keepAlive = setInterval(chrome.runtime.getPlatformInfo, 25 * 1000);
try {
await promise;
} finally {
clearInterval(keepAlive);
}
}
waitUntil(someExpensiveCalculation());
Zorg ervoor dat een hulpverlener continu in leven blijft.
In zeldzame gevallen is het nodig om de levensduur onbeperkt te verlengen. We hebben vastgesteld dat bedrijven en onderwijsinstellingen de grootste gebruiksscenario's zijn en staan dit specifiek in die context toe, maar we ondersteunen dit over het algemeen niet. In deze uitzonderlijke omstandigheden kan een service worker actief worden gehouden door periodiek een eenvoudige API-aanroep naar een extensie te doen. Het is belangrijk om te benadrukken dat deze aanbeveling alleen geldt voor extensies die draaien op beheerde apparaten voor zakelijke of educatieve doeleinden. In andere gevallen is dit niet toegestaan en het Chrome-extensieteam behoudt zich het recht voor om in de toekomst actie te ondernemen tegen dergelijke extensies.
Gebruik het volgende codefragment om uw service worker actief te houden:
/**
* 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'];
}