Gestire gli eventi con i service worker

Tutorial che tratta i concetti del service worker delle estensioni

Panoramica

Questo tutorial fornisce un'introduzione ai service worker 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 la omnibox. Imparerai a:

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

Prima di iniziare

La presente guida presuppone che tu abbia esperienza di base nello sviluppo web. Ti consigliamo di consultare Estensioni 101 e Hello World per un'introduzione allo sviluppo delle estensioni.

Crea l'estensione

Inizia creando una nuova directory denominata quick-api-reference in cui inserire i file delle estensioni oppure scarica il codice sorgente dal nostro repository di esempi di GitHub.

Passaggio 1: registra il service worker

Crea il file manifest nella directory principale 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 proprio service worker nel manifest, che richiede un solo file JavaScript. Non c'è bisogno di chiamare navigator.serviceWorker.register(), come faresti in una pagina web.

Crea una cartella images e scarica le icone al suo interno.

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

Passaggio 2: importa più moduli del service worker

Il nostro service worker implementa due funzionalità. Per una migliore gestibilità, implementeremo ogni funzionalità in un modulo separato. Occorre innanzitutto dichiarare il service worker come ES Module nel file manifest, il che ci consente di importare 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 a ciascuno un log della console.

sw-omnibox.js:

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

sw-tips.js:

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

Per scoprire altri metodi per importare più file in un service worker, consulta la sezione Importazione degli script.

Facoltativo: 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 viene visualizzato "service worker (inattivo)", a indicare che il service worker è stato terminato. Fai clic sul link "Service worker (non attivo)" per ispezionarlo. L'animazione seguente mostra questa situazione.

Hai notato che l'ispezione del service worker lo ha riattivato? 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 scoprire dove individuare gli errori. Un modo per farlo è eliminare ".js" dall'importazione './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.

Vedi Debug delle estensioni per scoprire altri modi per eseguire il debug del service worker delle estensioni.

Passaggio 4: inizializza lo stato

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

manifest.json:

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

Per prima cosa, salva i suggerimenti predefiniti nello spazio di archiviazione. Possiamo inizializzare lo stato quando l'estensione viene installata per la prima volta 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 terminati ripetutamente durante la sessione del browser di un utente, il che li rende incompatibili con le variabili globali. Utilizza invece chrome.storage.local, che archivia i dati sulla macchina locale.

Consulta l'articolo Rendere persistenti i dati invece di utilizzare variabili globali per scoprire altre opzioni di archiviazione per i service worker dell'estensione.

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 in funzioni asincrone. In questo modo Chrome può garantire che tutti i gestori di eventi vengano ripristinati in caso di riavvio di un service worker.

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

manifest.json:

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

Ora registra i listener di eventi della omnibox al livello superiore dello script. Quando l'utente inserisce la parola chiave omnibox (api) nella barra degli indirizzi seguita da Tab o dallo spazio, Chrome mostra un elenco di suggerimenti basati sulle parole chiave nello spazio di archiviazione. L'evento onInputChanged(), che utilizza 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 avrà selezionato 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() riceve l'input della omnibox e la 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: imposta un evento ricorrente

I metodi setTimeout() o setInterval() vengono comunemente utilizzati per eseguire attività ritardate o periodiche. Tuttavia, queste API possono avere esito negativo perché lo scheduler annullerà i timer quando il worker del servizio viene arrestato. Le estensioni possono utilizzare l'API chrome.alarms.

Per iniziare, richiedi l'autorizzazione "alarms" nel manifest. Inoltre, per recuperare i suggerimenti per l'estensione da una posizione ospitata remota, devi richiedere l'autorizzazione host:

manifest.json:

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

L'estensione recupererà tutti i suggerimenti, ne selezionerà 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 quindi verificare se la sveglia esiste e, in caso contrario, crearla.

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 con altri contesti

Le estensioni utilizzano 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 la punta del giorno. Invia un messaggio per richiedere il suggerimento del giorno al service worker.

Per iniziare, dichiara lo script dei contenuti nel 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 seguente codice invia un messaggio al service worker con la richiesta del suggerimento. A questo punto, aggiunge un pulsante che aprirà un popover contenente il suggerimento per l'estensione. Questo codice utilizza l'API Popover della nuova 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 un gestore dei messaggi al nostro service worker che invii una risposta allo script dei contenuti con il suggerimento del giorno.

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 progetto sia simile alla seguente:

I contenuti della cartella dell&#39;estensione: cartella images, 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 passate
  4. Si aprirà una nuova pagina con la pagina di riferimento dell'API di Chrome.

Dovrebbe avere il seguente aspetto:

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

Apri la punta del giorno

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

Apri suggerimento giornaliero
Estensione API rapida che apre la punta del giorno.

🎯 Potenziali miglioramenti

In base a quanto appreso oggi, prova a realizzare 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 un'altra pagina alle pagine dell'API di riferimento per le estensioni web dell'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 Inserire automaticamente un elemento in un insieme specifico di pagine.
Gestione schede Per creare un popup che gestisca 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 percorso di apprendimento del service worker dell'estensione, ti consigliamo di consultare i seguenti articoli: