Tutorial que aborda conceitos de service worker de extensão
Visão geral
Este tutorial apresenta os service workers de extensões do Chrome. Como parte deste tutorial, você vai criar uma extensão que permite aos usuários navegar rapidamente até as páginas de referência da API Chrome usando a omnibox. Você aprenderá o seguinte:
- Registre o service worker e importe módulos.
- Depure o service worker da extensão.
- Gerenciar estado e processar eventos.
- Acionar eventos periódicos.
- Comunicar com scripts de conteúdo.
Antes de começar
Neste guia, presumimos que você tenha experiência básica em desenvolvimento da Web. Recomendamos que você leia Noções básicas sobre extensões e Hello World para uma introdução ao desenvolvimento de extensões.
Criar a extensão
Comece criando um novo diretório chamado quick-api-reference
para armazenar os arquivos de extensão ou
faça o download do código-fonte no repositório de exemplos do GitHub.
Etapa 1: registrar o service worker
Crie o arquivo manifest na raiz do projeto e adicione o seguinte código:
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"
}
}
As extensões registram o service worker no manifesto, que usa apenas um arquivo JavaScript.
Não é necessário chamar navigator.serviceWorker.register()
, como faria em uma página da Web.
Crie uma pasta images
e faça o download dos ícones nela.
Confira as primeiras etapas do tutorial "Tempo de leitura" para saber mais sobre os metadados e ícones da extensão no manifesto.
Etapa 2: importar vários módulos de service worker
Nosso service worker implementa dois recursos. Para facilitar a manutenção, vamos implementar cada recurso em um módulo separado. Primeiro, precisamos declarar o service worker como um módulo ES no manifesto, o que permite importar módulos no service worker:
manifest.json:
{
"background": {
"service_worker": "service-worker.js",
"type": "module"
},
}
Crie o arquivo service-worker.js
e importe dois módulos:
import './sw-omnibox.js';
import './sw-tips.js';
Crie esses arquivos e adicione um registro do console a cada um deles.
sw-omnibox.js:
console.log("sw-omnibox.js");
sw-tips.js:
console.log("sw-tips.js");
Consulte Como importar scripts para saber mais sobre outras maneiras de importar vários arquivos em um service worker.
Opcional: depurar o service worker
Vou explicar como encontrar os registros do service worker e saber quando ele foi encerrado. Primeiro, siga as instruções para carregar uma extensão descompactada.
Após 30 segundos, você vai ver "service worker (inactive)", o que significa que o service worker foi encerrado. Clique no link "service worker (inactive)" para inspecionar. A animação a seguir mostra isso.
Você percebeu que inspecionar o service worker o ativou? Abrir o service worker nas devtools o mantém ativo. Para garantir que a extensão se comporte corretamente quando o service worker for encerrado, feche o DevTools.
Agora, divida a extensão para saber onde localizar erros. Uma maneira de fazer isso é excluir ".js" da importação './sw-omnibox.js'
no arquivo service-worker.js
. O Chrome não poderá registrar o service worker.
Volte para chrome://extensions e atualize a extensão. Você vai encontrar dois erros:
Service worker registration failed. Status code: 3.
An unknown error occurred when fetching the script.
Consulte Depuração de extensões para mais maneiras de depurar o service worker de extensão.
Etapa 4: inicializar o estado
O Chrome encerra os service workers se eles não forem necessários. Usamos a API chrome.storage
para manter o estado nas sessões do service worker. Para acesso ao armazenamento, precisamos solicitar permissão no manifesto:
manifest.json:
{
...
"permissions": ["storage"],
}
Primeiro, salve as sugestões padrão no armazenamento. Podemos inicializar o estado quando a extensão é instalada pela primeira vez detectando o 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']
});
}
});
Os service workers não têm acesso direto ao objeto window e, portanto, não podem usar
window.localStorage
para armazenar valores. Além disso, os service workers são ambientes de execução de curta duração.
Eles são encerrados repetidamente durante a sessão do navegador de um usuário, o que os torna incompatíveis com
variáveis globais. Em vez disso, use chrome.storage.local
, que armazena dados na máquina local.
Consulte Persistir dados em vez de usar variáveis globais para saber mais sobre outras opções de armazenamento para service workers de extensão.
Etapa 5: registrar seus eventos
Todos os listeners de eventos precisam ser registrados de forma estática no escopo global do service worker. Em outras palavras, os listeners de eventos não podem ser aninhados em funções assíncronas. Assim, o Chrome pode garantir que todos os manipuladores de eventos sejam restaurados em caso de reinicialização de um service worker.
Neste exemplo, vamos usar a API chrome.omnibox
, mas primeiro precisamos declarar o gatilho de palavra-chave da omnibox no manifesto:
manifest.json:
{
...
"minimum_chrome_version": "102",
"omnibox": {
"keyword": "api"
},
}
Agora, registre os listeners de eventos da omnibox no nível superior do script. Quando o usuário digita a palavra-chave da omnibox (api
) na barra de endereço seguida de tabulação ou espaço, o Chrome mostra uma lista de sugestões com base nas palavras-chave armazenadas. O evento onInputChanged()
, que usa a entrada do usuário atual e um objeto suggestResult
, é responsável por preencher essas sugestões.
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);
});
Depois que o usuário selecionar uma sugestão, o onInputEntered()
vai abrir a página de referência da API do Chrome correspondente.
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);
});
A função updateHistory()
recebe a entrada da omnibox e a salva em storage.local
. Assim, o termo de pesquisa mais recente pode ser usado depois como uma sugestão da 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 });
}
Etapa 6: configurar um evento recorrente
Os métodos setTimeout()
ou setInterval()
são usados com frequência para realizar tarefas atrasadas ou periódicas. No entanto, essas APIs podem falhar porque o programador cancela os timers quando o service
worker é encerrado. Em vez disso, as extensões podem usar a API chrome.alarms
.
Comece solicitando a permissão "alarms"
no manifesto:
manifest.json:
{
...
"permissions": ["storage"],
"permissions": ["storage", "alarms"],
}
A extensão vai buscar todas as dicas, escolher uma aleatoriamente e salvar no armazenamento. Vamos criar um alarme que será acionado uma vez por dia para atualizar a dica. Os alarmes não são salvos quando você fecha o Chrome. Portanto, precisamos verificar se o alarme existe e criá-lo se não existir.
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);
Etapa 7: comunicar-se com outros contextos
As extensões usam scripts de conteúdo para ler e modificar o conteúdo da página. Quando um usuário visita uma página de referência da API Chrome, o script de conteúdo da extensão atualiza a página com a dica do dia. Ele envia uma mensagem para solicitar a dica do dia do service worker.
Comece declarando o script de conteúdo no manifesto e adicione o padrão de correspondência correspondente à documentação de referência da API Chrome.
manifest.json:
{
...
"content_scripts": [
{
"matches": ["https://developer.chrome.com/docs/extensions/reference/*"],
"js": ["content.js"]
}
]
}
Crie um arquivo de conteúdo. O código a seguir envia uma mensagem ao service worker solicitando a dica. Em seguida, adiciona um botão que abre um popover com a dica da extensão. Esse código usa a nova API Popover da plataforma da 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;
}
A etapa final é adicionar um gerenciador de mensagens ao service worker que envia uma resposta ao script de conteúdo com a dica diária.
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;
}
});
Testar se ele funciona
Verifique se a estrutura de arquivos do seu projeto é semelhante a esta:
Carregar a extensão localmente
Para carregar uma extensão descompactada no modo de desenvolvedor, siga as etapas em Hello world.
Abrir uma página de referência
- Digite a palavra-chave "api" na barra de endereço do navegador.
- Pressione "tab" ou "espaço".
- Insira o nome completo da API.
- OU escolha uma opção na lista de pesquisas anteriores
- Uma nova página será aberta com a página de referência da API Chrome.
Ele será parecido com o seguinte:

Abrir a dica do dia
Clique no botão "Dica" na barra de navegação para abrir a dica da extensão.

🎯 Melhorias possíveis
Com base no que você aprendeu hoje, tente fazer o seguinte:
- Use outra maneira de implementar as sugestões da omnibox.
- Crie seu próprio modal personalizado para mostrar a dica da extensão.
- Abra outra página para as páginas de referência da API Web Extensions da MDN.
Continue criando!
Parabéns por concluir este tutorial 🎉. Continue aprimorando suas habilidades com outros tutoriais para iniciantes:
Extensão | O que você vai aprender |
---|---|
Tempo de leitura | Para inserir um elemento em um conjunto específico de páginas automaticamente. |
Gerenciador de guias | Para criar um pop-up que gerencia guias do navegador. |
Modo sem distrações | Para executar o código na página atual depois de clicar na ação da extensão. |
Continue descobrindo
Para continuar o programa de aprendizado sobre service workers de extensão, recomendamos que você leia os seguintes artigos: