Gestire gli eventi con i service worker

Tutorial che illustra i concetti relativi ai worker del servizio di estensione

Panoramica

Questo tutorial fornisce un'introduzione ai worker di servizio delle estensioni di Chrome. Nell'ambito di questo tutorial, creerai un'estensione che consente agli utenti di accedere rapidamente alle pagine di riferimento dell'API Chrome utilizzando l'omnibox. Imparerai come:

  • Registra il tuo service worker e importa i moduli.
  • Esegui il debug del worker del servizio di estensione.
  • Gestisci lo stato e gestisci gli eventi.
  • Attivare eventi periodici.
  • Comunicare con gli script di contenuti.

Prima di iniziare

Questa guida presuppone che tu abbia esperienza di base nello sviluppo web. Per un'introduzione allo sviluppo di estensioni, ti consigliamo di consultare Estensioni 101 e Hello World.

Crea l'estensione

Per iniziare, crea una nuova directory denominata quick-api-reference per contenere i file di estensione o scarica il codice sorgente dal nostro repository di esempi GitHub.

Passaggio 1: registra il service worker

Crea il file manifest nella directory principale del progetto e aggiungi il seguente codice:

manifest.json:

{
  "manifest_version": 3,
  "name": "Open extension API reference",
  "version": "1.0.0",
  "icons": {
    "16": "images/icon-16.png",
    "128": "images/icon-128.png"
  },
  "background": {
    "service_worker": "service-worker.js"
  }
}

Le estensioni registrano il proprio service worker nel file manifest, che richiede un solo file JavaScript. Non è necessario chiamare navigator.serviceWorker.register(), come faresti in una pagina web.

Crea una cartella images, quindi scarica le icone al suo interno.

Consulta i primi passaggi del tutorial Tempo di lettura per scoprire di più sui metadata e sulle icone dell'estensione nel file manifest.

Passaggio 2: importa più moduli di worker di servizio

Il nostro worker di servizio implementa due funzionalità. Per una migliore manutenibilità, implementeremo ogni funzionalità in un modulo separato. Innanzitutto, dobbiamo dichiarare il service worker come modulo ES nel file manifest, in modo da poter importare i moduli nel service worker:

manifest.json:

{
 "background": {
    "service_worker": "service-worker.js",
    "type": "module"
  },
}

Crea il file service-worker.js e importa due moduli:

import './sw-omnibox.js';
import './sw-tips.js';

Crea questi file e aggiungi un log della console a ciascuno.

sw-omnibox.js:

console.log("sw-omnibox.js");

sw-tips.js:

console.log("sw-tips.js");

Consulta Importazione di script per scoprire altri modi per importare più file in un servizio worker.

(Facoltativo) Debug del service worker

Ti spiegherò come trovare i log del servizio worker e sapere quando è stato terminato. Innanzitutto, segui le istruzioni per caricare un'estensione scompattata.

Dopo 30 secondi viene visualizzato il messaggio "service worker (inactive)", che indica che il service worker è stato terminato. Fai clic sul link "service worker (inactive)" per ispezionarlo. La seguente animazione lo dimostra.

Hai notato che l'ispezione del service worker lo ha svegliato? Se apri il service worker in devtools, questo rimarrà attivo. Per assicurarti che l'estensione si comporti correttamente quando il tuo worker di servizio viene terminato, ricordati di chiudere DevTools.

Ora suddividi l'estensione per scoprire dove individuare gli errori. Un modo per farlo è eliminare ".js" dall'importazione di './sw-omnibox.js' nel file service-worker.js. Chrome non potrà registrare il service worker.

Torna a chrome://extensions e aggiorna l'estensione. Vedrai due errori:

Service worker registration failed. Status code: 3.

An unknown error occurred when fetching the script.

Per scoprire altri modi per eseguire il debug del service worker dell'estensione, consulta Eseguire il debug delle estensioni.

Passaggio 4: inizializza lo stato

Chrome arresterà i worker di servizio se non sono necessari. Utilizziamo l'API chrome.storage per mantenere lo stato nelle sessioni dei worker di servizio. Per l'accesso allo spazio di archiviazione, dobbiamo richiedere l'autorizzazione nel file manifest:

manifest.json:

{
  ...
  "permissions": ["storage"],
}

Innanzitutto, salva i suggerimenti predefiniti nello spazio di archiviazione. Possiamo inizializzare lo stato al primo avvio dell'estensione ascoltando l'evento runtime.onInstalled():

sw-omnibox.js:

...
// Save default API suggestions
chrome.runtime.onInstalled.addListener(({ reason }) => {
  if (reason === 'install') {
    chrome.storage.local.set({
      apiSuggestions: ['tabs', 'storage', 'scripting']
    });
  }
});

I worker di servizio non hanno accesso diretto all'oggetto window e pertanto non possono utilizzare window.localStorage per memorizzare i valori. Inoltre, i worker di servizio sono ambienti di esecuzione di breve durata; vengono interrotti ripetutamente durante la sessione del browser di un utente, il che li rende incompatibili con le variabili globali. Utilizza invece chrome.storage.local, che memorizza i dati sulla macchina locale.

Consulta Mantenere i dati in modo permanente anziché utilizzare le variabili globali per scoprire altre opzioni di archiviazione per i worker di servizio delle estensioni.

Passaggio 5: registra gli eventi

Tutti gli ascoltatori di eventi devono essere registrati in modo statico nell'ambito globale del service worker. In altre parole, gli ascoltatori di eventi non devono essere nidificati in funzioni asincrone. In questo modo, Chrome può assicurarsi che tutti i gestori degli eventi vengano ripristinati in caso di riavvio del servizio worker.

In questo esempio utilizzeremo l'API chrome.omnibox, ma prima dobbiamo dichiarare l'attivatore delle parole chiave per l'omnibox nel file manifest:

manifest.json:

{
  ...
  "minimum_chrome_version": "102",
  "omnibox": {
    "keyword": "api"
  },
}

Ora, registra gli ascoltatori di eventi dell'omnibox al livello superiore dello script. Quando l'utente inserisce la parola chiave dell'omnibox (api) nella barra degli indirizzi seguita da Tab o uno spazio, Chrome mostra un elenco di suggerimenti in base alle parole chiave memorizzate. L'evento onInputChanged(), che riceve l'input dell'utente corrente e un oggetto suggestResult, è responsabile della compilazione di questi suggerimenti.

sw-omnibox.js:

...
const URL_CHROME_EXTENSIONS_DOC =
  'https://developer.chrome.com/docs/extensions/reference/';
const NUMBER_OF_PREVIOUS_SEARCHES = 4;

// Display the suggestions after user starts typing
chrome.omnibox.onInputChanged.addListener(async (input, suggest) => {
  await chrome.omnibox.setDefaultSuggestion({
    description: 'Enter a Chrome API or choose from past searches'
  });
  const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
  const suggestions = apiSuggestions.map((api) => {
    return { content: api, description: `Open chrome.${api} API` };
  });
  suggest(suggestions);
});

Dopo che l'utente ha selezionato un suggerimento, onInputEntered() aprirà la pagina di riferimento dell'API Chrome corrispondente.

sw-omnibox.js:

...
// Open the reference page of the chosen API
chrome.omnibox.onInputEntered.addListener((input) => {
  chrome.tabs.create({ url: URL_CHROME_EXTENSIONS_DOC + input });
  // Save the latest keyword
  updateHistory(input);
});

La funzione updateHistory() acquisisce l'input dell'omnibox e lo salva in storage.local. In questo modo, il termine di ricerca più recente può essere utilizzato in un secondo momento come suggerimento dell'omnibox.

sw-omnibox.js:

...
async function updateHistory(input) {
  const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
  apiSuggestions.unshift(input);
  apiSuggestions.splice(NUMBER_OF_PREVIOUS_SEARCHES);
  return chrome.storage.local.set({ apiSuggestions });
}

Passaggio 6: configura un evento ricorrente

I metodi setTimeout() o setInterval() vengono comunemente utilizzati per eseguire attività ritardate o periodiche. Tuttavia, queste API possono non riuscire perché il programmatore annulla i timer al termine del servizio lavorare. Le estensioni possono invece utilizzare l'API chrome.alarms.

Inizia richiedendo l'autorizzazione "alarms" nel file manifest. Inoltre, per recuperare i suggerimenti dell'estensione da una posizione ospitata in remoto, devi richiedere l'autorizzazione host:

manifest.json:

{
  ...
  "permissions": ["storage"],
  "permissions": ["storage", "alarms"],
  "host_permissions": ["https://chrome.dev/f/*"],
}

L'estensione recupererà tutti i suggerimenti, ne sceglierà uno a caso e lo salverà nello spazio di archiviazione. Creeremo una sveglia che verrà attivata una volta al giorno per aggiornare il suggerimento. Gli allarmi non vengono salvati quando chiudi Chrome. Dobbiamo quindi verificare se l'allarme esiste e crearlo se non è presente.

sw-tips.js:

// Fetch tip & save in storage
const updateTip = async () => {
  const response = await fetch('https://chrome.dev/f/extension_tips');
  const tips = await response.json();
  const randomIndex = Math.floor(Math.random() * tips.length);
  return chrome.storage.local.set({ tip: tips[randomIndex] });
};

const ALARM_NAME = 'tip';

// Check if alarm exists to avoid resetting the timer.
// The alarm might be removed when the browser session restarts.
async function createAlarm() {
  const alarm = await chrome.alarms.get(ALARM_NAME);
  if (typeof alarm === 'undefined') {
    chrome.alarms.create(ALARM_NAME, {
      delayInMinutes: 1,
      periodInMinutes: 1440
    });
    updateTip();
  }
}

createAlarm();

// Update tip once a day
chrome.alarms.onAlarm.addListener(updateTip);

Passaggio 7: comunica con altri contesti

Le estensioni utilizzano gli script di contenuti per leggere e modificare i contenuti della pagina. Quando un utente visita una pagina di riferimento dell'API di Chrome, lo script dei contenuti dell'estensione aggiorna la pagina con il suggerimento del giorno. Invia un messaggio per richiedere il suggerimento del giorno al service worker.

Inizia dichiarando lo script dei contenuti nel file manifest e aggiungi il pattern di corrispondenza corrispondente alla documentazione di riferimento dell'API Chrome.

manifest.json:

{
  ...
  "content_scripts": [
    {
      "matches": ["https://developer.chrome.com/docs/extensions/reference/*"],
      "js": ["content.js"]
    }
  ]
}

Crea un nuovo file di contenuti. Il codice seguente invia un messaggio al service worker che richiede il suggerimento. Aggiungi un pulsante che apra un popup contenente il suggerimento dell'estensione. Questo codice utilizza la nuova API Popup della piattaforma web.

content.js:

(async () => {
  // Sends a message to the service worker and receives a tip in response
  const { tip } = await chrome.runtime.sendMessage({ greeting: 'tip' });

  const nav = document.querySelector('.upper-tabs > nav');
  
  const tipWidget = createDomElement(`
    <button type="button" popovertarget="tip-popover" popovertargetaction="show" style="padding: 0 12px; height: 36px;">
      <span style="display: block; font: var(--devsite-link-font,500 14px/20px var(--devsite-primary-font-family));">Tip</span>
    </button>
  `);

  const popover = createDomElement(
    `<div id='tip-popover' popover style="margin: auto;">${tip}</div>`
  );

  document.body.append(popover);
  nav.append(tipWidget);
})();

function createDomElement(html) {
  const dom = new DOMParser().parseFromString(html, 'text/html');
  return dom.body.firstElementChild;
}

Il passaggio finale consiste nell'aggiungere al nostro service worker un gestore dei messaggi che invii una risposta allo script dei contenuti con il suggerimento giornaliero.

sw-tips.js:

...
// Send tip to content script via messaging
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.greeting === 'tip') {
    chrome.storage.local.get('tip').then(sendResponse);
    return true;
  }
});

Verifica che funzioni

Verifica che la struttura del file del progetto sia la seguente:

I contenuti della cartella dell&#39;estensione: cartella immagini, manifest.json, service-worker.js, sw-omnibox.js, sw-tips.js e content.js

Carica l'estensione localmente

Per caricare un'estensione non pacchettizzata in modalità sviluppatore, segui i passaggi descritti in Hello world.

Aprire una pagina di riferimento

  1. Inserisci la parola chiave "api" nella barra degli indirizzi del browser.
  2. Premi "Tab" o "Barra spaziatrice".
  3. Inserisci il nome completo dell'API.
    • OPPURE scegli da un elenco di ricerche precedenti
  4. Si aprirà una nuova pagina con il riferimento all'API Chrome.

Dovrebbe avere il seguente aspetto:

Riferimento rapido all&#39;API che apre il riferimento all&#39;API di runtime
Estensione API rapida che apre l'API Runtime.

Aprire il suggerimento del giorno

Fai clic sul pulsante Suggerimento nella barra di navigazione per aprire il suggerimento dell'estensione.

Apri il consiglio giornaliero in
Estensione API rapida che apre il suggerimento del giorno.

🎯 Potenziali miglioramenti

In base a quanto appreso oggi, prova a svolgere una delle seguenti attività:

  • Scopri un altro modo per implementare i suggerimenti dell'omnibox.
  • Crea la tua finestra modale personalizzata per visualizzare il suggerimento dell'estensione.
  • Apri un'altra pagina delle pagine API di riferimento delle estensioni web di MDN.

Continua a costruire.

Congratulazioni per aver completato questo tutorial 🎉. Continua a migliorare le tue competenze completando altri tutorial per principianti:

Estensione Cosa imparerai a fare
Tempo di lettura Per inserire automaticamente un elemento in un insieme specifico di pagine.
Tabs Manager Per creare un popup che gestisce le schede del browser.
Modalità Niente distrazioni Per eseguire il codice nella pagina corrente dopo aver fatto clic sull'azione dell'estensione.

Continua a esplorare

Per continuare il tuo percorso di apprendimento degli addetti ai servizi di estensione, ti consigliamo di consultare i seguenti articoli: