Gestire gli eventi con i service worker

Tutorial che illustra i concetti del service worker di estensione

Panoramica

Questo tutorial fornisce un'introduzione ai service worker delle estensioni di Chrome. Nell'ambito di questo imparerai a creare un'estensione che consente agli utenti di accedere rapidamente al riferimento API di Chrome pagine utilizzando la omnibox. Imparerai come:

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

Prima di iniziare

Questa guida presuppone che tu abbia esperienza di base di sviluppo web. Ti consigliamo di consultare Estensioni 101 e Hello World per un'introduzione a lo sviluppo di estensioni.

Crea l'estensione

Inizia creando una nuova directory chiamata quick-api-reference in cui inserire i file dell'estensione, oppure scarica il codice sorgente dal nostro repository di esempi GitHub.

Passaggio 1: registra il service worker

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

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 service worker nel manifest, che accetta 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.

Dai un'occhiata ai primi passaggi del tutorial Tempo di lettura per scoprire di più sui metadati e sulle icone del file manifest dell'estensione.

Passaggio 2: importa più moduli del service worker

Il nostro service worker implementa due funzionalità. Per una migliore manutenibilità, implementeremo ciascuna funzionalità in un modulo separato. Innanzitutto, dobbiamo dichiarare il service worker come modulo ES nel nostro file manifest, che ci consente di importare moduli nel nostro 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 degli script per conoscere altri modi per importare più file in un service worker.

(Facoltativo) Eseguire il debug del service worker

Spiegherò come trovare i log del service worker e sapere quando è terminato. Innanzitutto, segui le istruzioni per caricare un'estensione non pacchettizzata.

Dopo 30 secondi, visualizzerai il messaggio "Service worker (non attivo)". che indica che il service worker è stato arrestato. Fai clic sul "service worker (non attivo)" per esaminarlo. L'animazione seguente mostra l'immagine.

Hai notato che l'ispezione del service worker l'ha riattivata? L'apertura del service worker in DevTools lo manterrà attivo. Per assicurarti che l'estensione funzioni correttamente quando il service worker viene arrestato, ricordati di chiudere DevTools.

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

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

Service worker registration failed. Status code: 3.

An unknown error occurred when fetching the script.

Per conoscere altri modi in cui eseguire il debug del service worker delle estensioni, consulta Debug delle estensioni.

Passaggio 4: inizializza lo stato

Chrome disattiverà i Service worker se non sono necessari. Utilizziamo l'API chrome.storage per mantenere lo stato tra le sessioni del service worker. Per accedere allo spazio di archiviazione, dobbiamo richiedere l'autorizzazione nel file manifest:

manifest.json:

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

Prima di tutto, salva i suggerimenti predefiniti nello spazio di archiviazione. Possiamo inizializzare lo stato alla prima installazione 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 Service worker non hanno accesso diretto all'oggetto finestra e pertanto non possono utilizzare window.localStorage per archiviare i valori. Inoltre, i service worker sono ambienti di esecuzione di breve durata; vengono chiusi ripetutamente nel corso della sessione del browser di un utente, il che li rende incompatibili con variabili globali. Utilizza invece chrome.storage.local, che archivia i dati sulla macchina locale.

Per saperne di più sulle altre opzioni di archiviazione per i service worker di estensione, consulta l'articolo Persistere i dati anziché utilizzare le variabili globali.

Passaggio 5: registra i tuoi eventi

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

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

manifest.json:

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

Ora registra i listener di eventi nella omnibox al livello superiore dello script. Quando l'utente inserisce la parola chiave omnibox (api) nella barra degli indirizzi seguita da una scheda o da uno spazio, Chrome mostra un elenco di suggerimenti basati sulle parole chiave nello spazio di archiviazione. L'evento onInputChanged(), che accetta l'input 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 seleziona un suggerimento, onInputEntered() aprirà la pagina di riferimento dell'API di 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() prende l'input della 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 nella 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() sono comunemente utilizzati per eseguire operazioni ritardate o periodiche attività di machine learning. Tuttavia, queste API possono non riuscire perché lo scheduler annullerà i timer quando il servizio worker è stato interrotto. Le estensioni possono invece utilizzare l'API chrome.alarms.

Inizia richiedendo l'autorizzazione "alarms" nel file manifest. Inoltre, per recuperare i suggerimenti per le estensioni da una posizione ospitata remota, devi richiedere l'autorizzazione di accesso all'host:

manifest.json:

{
  ...
  "permissions": ["storage"],
  "permissions": ["storage", "alarms"],
  "host_permissions": ["https://extension-tips.glitch.me/*"],
}

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. Le sveglie non vengono salvate alla chiusura di Chrome. Dobbiamo verificare se l'allarme esiste e crearlo in caso contrario.

sw-tips.js:

// Fetch tip & save in storage
const updateTip = async () => {
  const response = await fetch('https://extension-tips.glitch.me/tips.json');
  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 in 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 consiglio del giorno. Invia un messaggio per richiedere la mancia del giorno al service worker.

Per prima cosa, dichiara lo script dei contenuti nel file manifest e aggiungi il pattern di corrispondenza corrispondente alla documentazione di riferimento dell'API di Chrome.

manifest.json:

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

Crea un nuovo file di contenuti. Il codice riportato di seguito invia un messaggio al service worker richiedendo la mancia. A questo punto, aggiungi un pulsante che aprirà un popover contenente la mancia dell'estensione. Questo codice utilizza la nuova piattaforma web API Popover.

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 dei file del tuo progetto abbia il seguente aspetto:

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

Caricare l'estensione localmente

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

Apri una pagina di riferimento

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

Dovrebbe avere il seguente aspetto:

Riferimento rapido API per l&#39;apertura del riferimento dell&#39;API runtime
. Estensione API rapida che apre l'API Runtime.

Apri il consiglio del giorno

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

Apri suggerimento giornaliero tra
. Estensione API rapida che apre il consiglio del giorno.
di Gemini Advanced.
.

🎯 Potenziali miglioramenti

In base a quanto appreso oggi, prova a eseguire una delle seguenti azioni:

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

Continua a costruire!

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

Estensione Cosa imparerai a fare
Tempo di lettura Per inserire automaticamente un elemento in un insieme specifico di pagine.
Gestione schede Per creare un popup per la gestione delle schede del browser.
Modalità Niente distrazioni Eseguire il codice nella pagina corrente dopo aver fatto clic sull'azione di estensione.

Continua a esplorare

Per continuare il percorso di apprendimento del Service worker delle estensioni, ti consigliamo di consultare i seguenti articoli: