O pacote workbox-window
é um conjunto de módulos que se destinam a ser executados no
window
contexto, que
nas suas páginas da Web. Eles são um complemento para a outra caixa de trabalho
pacotes executados no service worker.
Os principais recursos/metas do workbox-window
são:
- Simplificar o processo de registro e atualização de service workers, ajudando desenvolvedores a identificar os momentos mais críticos no ciclo de vida do service worker e ao facilitar de responder a esses momentos.
- Para ajudar a evitar que os desenvolvedores cometem os erros mais comuns.
- Para facilitar a comunicação. entre o código em execução no service worker e o código em execução na janela.
Como importar e usar a janela de caixa de trabalho
O ponto de entrada principal do pacote workbox-window
é a classe Workbox
.
você pode importá-lo em seu código a partir de nossa CDN ou usando qualquer um dos
ferramentas de agrupamento de JavaScript.
Como usar nossa CDN
A maneira mais fácil de importar a classe Workbox
no seu site é usando nossa CDN:
<script type="module">
import {Workbox} from 'https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-window.prod.mjs';
if ('serviceWorker' in navigator) {
const wb = new Workbox('/sw.js');
wb.register();
}
</script>
Este exemplo usa <script type="module">
e a instrução import
para
carregar a classe Workbox
. Embora você possa pensar
que precisa transcompilar isso
código para fazê-lo funcionar em navegadores mais antigos, isso não é necessário.
Todos os principais navegadores que oferecem suporte ao service worker também oferecem suporte a módulos JavaScript nativos, por isso é perfeitamente não há problema em exibir esse código em qualquer navegador (navegadores mais antigos o ignoram).
Como carregar a caixa de trabalho com bundlers do JavaScript
Nenhuma ferramenta é necessária para usar o workbox-window
.
de desenvolvimento já inclui um bundler como
webpack ou Rollup que funcione
com dependências de npm, é possível usá-las para
carregar workbox-window
.
A primeira etapa é
instalar
workbox-window
como uma dependência do aplicativo:
npm install workbox-window
Em seguida, em um dos arquivos JavaScript do seu aplicativo, use a caixa de trabalho import
referenciando o nome do pacote workbox-window
:
import {Workbox} from 'workbox-window';
if ('serviceWorker' in navigator) {
const wb = new Workbox('/sw.js');
wb.register();
}
Se o bundler oferecer suporte à divisão de código por meio de instruções de importação dinâmica, faça o seguinte:
também é possível carregar condicionalmente workbox-window
, o que ajuda a reduzir a
tamanho do pacote principal da página.
Embora workbox-window
seja bem pequeno, não há motivo para ele
precisam ser carregados com a lógica principal do aplicativo do seu site, como service workers,
por sua natureza, são um aprimoramento progressivo.
if ('serviceWorker' in navigator) {
const {Workbox} = await import('workbox-window');
const wb = new Workbox('/sw.js');
wb.register();
}
Conceitos de agrupamento avançados
Ao contrário dos pacotes de caixa de trabalho executados no service worker, os arquivos de build
referenciados por workbox-window
main
e
Campos module
em
package.json
são transcompilados para ES5. Isso os torna compatíveis
ferramentas de build, e algumas delas não permitem que os desenvolvedores transcompilem nada do
as dependências de node_module
.
Se o sistema de compilação permitir a transcompilação das dependências (ou se você não precisar transcompilar nenhum código), então é melhor importar um objeto arquivo de origem em vez do próprio pacote.
Aqui estão as várias maneiras de importar Workbox
, além de uma explicação sobre
o que cada uma retornará:
// Imports a UMD version with ES5 syntax
// (pkg.main: "build/workbox-window.prod.umd.js")
const {Workbox} = require('workbox-window');
// Imports the module version with ES5 syntax
// (pkg.module: "build/workbox-window.prod.es5.mjs")
import {Workbox} from 'workbox-window';
// Imports the module source file with ES2015+ syntax
import {Workbox} from 'workbox-window/Workbox.mjs';
Exemplos
Depois de importar a classe Workbox
, você pode usá-la para registrar e
interagem com o service worker. Aqui estão alguns exemplos de maneiras de usar
Workbox
no seu aplicativo:
Registrar um service worker e notificar o usuário na primeira vez que ele estiver ativo
Muitos aplicativos da Web usam service workers para pré-armazenar em cache os recursos para que seu app funcione off-line em carregamentos de página subsequentes. Em alguns casos, pode fazer sentido informar informando ao usuário que o app está disponível off-line.
const wb = new Workbox('/sw.js');
wb.addEventListener('activated', event => {
// `event.isUpdate` will be true if another version of the service
// worker was controlling the page when this version was registered.
if (!event.isUpdate) {
console.log('Service worker activated for the first time!');
// If your service worker is configured to precache assets, those
// assets should all be available now.
}
});
// Register the service worker after event listeners have been added.
wb.register();
Notifica o usuário se um service worker foi instalado, mas está travado, aguardando para ativar
Quando uma página controlada por um service worker existente registra um novo serviço por padrão, o service worker não será ativado até que todos os clientes controlados pelo service worker inicial foram totalmente descarregados.
Essa é uma fonte comum de confusão para os desenvolvedores, especialmente nos casos em que recarregar a página atual não faz com que o novo service worker seja ativado.
Para ajudar a minimizar a confusão e deixar claro quando essa situação está acontecendo,
a classe Workbox
fornece um evento waiting
que você pode detectar:
const wb = new Workbox('/sw.js');
wb.addEventListener('waiting', event => {
console.log(
`A new service worker has installed, but it can't activate` +
`until all tabs running the current version have fully unloaded.`
);
});
// Register the service worker after event listeners have been added.
wb.register();
Notificar o usuário sobre atualizações de cache do pacote workbox-broadcast-update
O pacote workbox-broadcast-update
(link em inglês) é uma ótima maneira de veicular conteúdo do cache (para entrega rápida) e, ao mesmo tempo,
informar o usuário sobre as atualizações do conteúdo (usando o
estratégia obsoleta enquanto revalidar).
Para receber essas atualizações pela janela, você pode detectar eventos message
de
tipo CACHE_UPDATED
:
const wb = new Workbox('/sw.js');
wb.addEventListener('message', event => {
if (event.data.type === 'CACHE_UPDATED') {
const {updatedURL} = event.data.payload;
console.log(`A newer version of ${updatedURL} is available!`);
}
});
// Register the service worker after event listeners have been added.
wb.register();
Enviar ao service worker uma lista de URLs a serem armazenados em cache
Para algumas aplicações, é possível conhecer todos os ativos que precisam ser armazenados em cache no momento da criação, mas alguns aplicativos exibem páginas completamente diferentes, com base no URL que o usuário acessa primeiro.
Para aplicativos na última categoria, faz sentido armazenar apenas os recursos em cache
o usuário precisava
para a página específica que ele visitou. Ao usar o botão
pacote workbox-routing
, será possível
enviará ao roteador uma lista de URLs a serem armazenados em cache, e ele armazenará esses URLs em cache de acordo
às regras definidas no próprio roteador.
Este exemplo envia uma lista de URLs carregados pela página para o roteador sempre que um um novo service worker é ativado. Não há problema em enviar todos os URLs, pois somente os URLs que correspondem a uma rota definida no service worker serão armazenados em cache:
const wb = new Workbox('/sw.js');
wb.addEventListener('activated', event => {
// Get the current page URL + all resources the page loaded.
const urlsToCache = [
location.href,
...performance.getEntriesByType('resource').map(r => r.name),
];
// Send that list of URLs to your router in the service worker.
wb.messageSW({
type: 'CACHE_URLS',
payload: {urlsToCache},
});
});
// Register the service worker after event listeners have been added.
wb.register();
Momentos importantes do ciclo de vida do service worker
O ciclo de vida do service worker é complexo e pode ser difícil de compreender totalmente. Parte do motivo tão complexa é que ela precisa lidar com todos os casos extremos para todos os usos possíveis de service worker (por exemplo, registrar mais de um service worker, registrar diferentes service workers em frames diferentes, registrando service workers com nomes diferentes etc.).
Mas a maioria dos desenvolvedores que implementam o service worker não precisa se preocupar com em todos esses casos extremos, porque o uso é bastante simples. Maioria dos desenvolvedores registram apenas um service worker por carregamento de página e não alteram o nome do service worker do arquivo que eles implantam no servidor.
A classe Workbox
adota essa visualização mais simples para o ciclo de vida do service worker
dividindo todos os registros de service workers em duas categorias: o
próprio service worker registrado e um service worker externo:
- Service worker registrado: um service worker que começou a ser instalado como um
quando a instância
Workbox
chamaregister()
ou a instância service worker se chamarregister()
não acionou um eventoupdatefound
no registro. - Service worker externo: um service worker que começou a instalação
seja qual for a instância de
Workbox
chamandoregister()
. Isso normalmente acontece quando um usuário tem uma nova versão do site aberta em outra guia. Quando um o evento tem origem em um service worker externo, o eventoisExternal
será definida comotrue
.
Com esses dois tipos de service workers em mente, aqui está um detalhamento de todos momentos importantes do ciclo de vida do service worker, além das recomendações para desenvolvedores para saber como lidar com eles:
Na primeira vez que um service worker é instalado
Você provavelmente vai querer acessar a primeira vez que um service worker diferente de como você tratará todas as atualizações futuras.
No workbox-window
, é possível diferenciar a versão primeiro
instalação e atualizações futuras verificando a propriedade isUpdate
em qualquer um
os seguintes eventos. Na primeira instalação, isUpdate
será
false
.
const wb = new Workbox('/sw.js');
wb.addEventListener('installed', event => {
if (!event.isUpdate) {
// First-installed code goes here...
}
});
wb.register();
Quando uma versão atualizada do service worker é encontrada
Quando um novo service worker começa a ser instalado, mas há uma versão atual
controlando a página, a propriedade isUpdate
de todos os eventos a seguir
ser true
.
Normalmente, a forma como você reage nessa situação é diferente da primeira porque você precisa gerenciar quando e como o usuário receberá a atualização.
Quando uma versão inesperada do service worker é encontrada
Às vezes, os usuários mantêm seu site aberto em uma guia em segundo plano por muito tempo tempo de resposta. Eles podem até mesmo abrir uma nova guia e navegar para seu site sem perceber seu site já estiver aberto em uma guia em segundo plano. Nesses casos, ter duas versões do seu site em execução ao mesmo tempo e que pode apresentar alguns problemas interessantes para você como desenvolvedor.
Considere um cenário em que há a guia A executando v1 do seu site e a guia B que executam a v2. Quando a guia B for carregada, ela será controlada pela versão do serviço worker enviado com a v1, mas a página retornada pelo servidor (se estiver usando um estratégia de armazenamento em cache com foco na rede para suas solicitações de navegação) conterá todos os recursos da v2.
Isso geralmente não é um problema para a aba B, já que quando você escreveu sua v2 você sabia como o código v1 funcionava. No entanto, pode ser problema para a aba A, já que o código v1 não poderia ter previsto mudanças que o código da v2 pode introduzir.
Para ajudar a lidar com essas situações, workbox-window
também envia instruções de ciclo de vida
quando detecta uma atualização de um servidor service worker, em que
"Externo" significa qualquer versão que não seja a atual Workbox
instância registrada.
A partir do Workbox v6, esses eventos são equivalentes aos eventos documentados
acima, com a adição de uma propriedade isExternal: true
definida em cada evento
objeto. Caso seu aplicativo da Web precise implementar lógica específica para lidar com uma
“externo” service worker, você pode verificar essa propriedade em seus manipuladores de eventos.
Como evitar erros comuns
Um dos recursos mais úteis que o Workbox oferece é a geração de registros do desenvolvedor. E
isso vale principalmente para workbox-window
.
Sabemos que desenvolver com um service worker muitas vezes pode ser confuso, e quando as coisas acontecer ao contrário do que você espera, pode ser difícil saber por quê.
Por exemplo, quando você faz uma alteração no service worker e atualiza a página, talvez você não veja essa alteração no navegador. A razão mais provável para isso é que o service worker ainda está aguardando para ser ativado.
No entanto, ao registrar um service worker na classe Workbox
,
informados sobre todas as mudanças de estado do ciclo de vida no console para desenvolvedores, o que deve
ajuda a depurar por que as coisas não estão conforme o esperado.
Além disso, um erro comum que os desenvolvedores cometem ao usar o service worker pela primeira vez é registrar um service worker na escopo errado.
Para ajudar a evitar que isso aconteça, a classe Workbox
avisará se o
página que registra o service worker não está no escopo desse service worker.
também avisa nos casos em que o service worker está ativo, mas ainda não
controlando a página:
Comunicação da janela para o service worker
O uso mais avançado do service worker envolve muitas mensagens entre os
service worker e a janela. A classe Workbox
também ajuda a fazer isso
fornecendo um método messageSW()
, que postMessage()
fará com que a instância
service worker registrado e aguardam uma resposta.
É possível enviar dados ao service worker em qualquer formato, mas o formato compartilhado por todos os pacotes do Workbox é um objeto com três propriedades (as duas últimas sendo opcional):
As mensagens enviadas pelo método messageSW()
usam MessageChannel
para que o receptor
possam responder a eles. Para responder a uma mensagem, você pode ligar
event.ports[0].postMessage(response)
no listener de eventos de mensagem. A
O método messageSW()
retorna uma promessa que será resolvida para qualquer response
ao responder.
Aqui está um exemplo de como enviar mensagens da janela para o service worker e
receber uma resposta. O primeiro bloco de código é o listener de mensagens na
service worker, e o segundo bloco usa a classe Workbox
para enviar as
e aguarde a resposta:
Código em sw.js:
const SW_VERSION = '1.0.0';
addEventListener('message', event => {
if (event.data.type === 'GET_VERSION') {
event.ports[0].postMessage(SW_VERSION);
}
});
Código em main.js (em execução na janela):
const wb = new Workbox('/sw.js');
wb.register();
const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);
Como gerenciar incompatibilidades de versão
O exemplo acima mostra como implementar a verificação do service worker versão na janela. Este exemplo é usado porque, quando você envia mensagens entre a janela e o service worker, é essencial tenha em mente que seu service worker pode não estar executando a mesma versão seu site com o código da página em execução e a solução para lidar com isso é diferente dependendo se a veiculação de suas páginas prioriza a rede ou que priorizam o cache.
Rede em primeiro lugar
Ao veicular sua rede de páginas primeiro, os usuários sempre receberão a versão mais recente do seu HTML do seu servidor. No entanto, na primeira vez que um usuário acessar seu site novamente (depois de você ter implantado uma atualização), o HTML que eles receberem será para a versão mais recente, mas o service worker em execução no navegador estará uma versão instalada anteriormente (possivelmente muitas versões antigas).
É importante entender essa possibilidade, porque, se o JavaScript for carregado, pela versão atual da sua página envia uma mensagem para uma versão mais antiga da sua do service worker, essa versão pode não saber como responder (ou pode responder com em um formato incompatível).
Por isso, é uma boa ideia sempre controlar a versão do service worker e verificar para versões compatíveis antes de qualquer trabalho crítico.
Por exemplo, no código acima, se a versão do service worker retornada por essa
A chamada messageSW()
é mais antiga que a versão esperada. É recomendável aguardar
até que uma atualização seja encontrada (o que vai acontecer quando você chamar register()
). Em
Nesse ponto, você pode notificar o usuário ou uma atualização, ou
pule a fase de espera
para ativar o novo service worker imediatamente.
Armazenar em cache primeiro
Ao contrário de quando você veicula páginas
priorizando a rede, ao exibir as páginas em cache,
primeiro, você sabe que, no início, sua página sempre terá a mesma versão
ao service worker, porque foi ele que o serviu. Por isso, é seguro
para usar o messageSW()
imediatamente.
No entanto, se uma versão atualizada do service worker for encontrada e ativada
quando sua página chamar register()
(ou seja, você pular a fase de espera intencionalmente),
talvez não seja mais seguro enviar mensagens para ele.
Uma estratégia para gerenciar essa possibilidade é usar um esquema de controle de versões que permite diferenciar atualizações interruptivas e não interruptivas, e, no caso de uma atualização interruptiva, você saberá que não é seguro enviar uma mensagem ao service worker. Em vez disso, avise o usuário de que ele está executando uma versão da página e sugerem que ele recarregue para obter a atualização.
Ajuda de pular espera
Uma convenção de uso comum para mensagens de janela para service worker é enviar um
Mensagem {type: 'SKIP_WAITING'}
para instruir um service worker instalado a
pular a fase de espera
e ativar.
A partir do Workbox v6, o método messageSkipWaiting()
pode ser usado para enviar uma
Mensagem {type: 'SKIP_WAITING'}
para o service worker em espera associado ao
o registro atual do service worker. Ele silenciosamente não fará nada se não houver
service worker em espera.
Tipos
Workbox
Uma classe para auxiliar no tratamento de registros, atualizações e reagir a eventos de ciclo de vida do service worker.
Propriedades
-
construtor
void
Cria uma nova instância do Workbox com um URL de script e um service worker . O URL e as opções do script são os mesmos usados chamando navigator.serviceWorker.register(scriptURL, options).
A função
constructor
tem esta aparência:(scriptURL: string | TrustedScriptURL, registerOptions?: object) => {...}
-
scriptURL
string | TrustedScriptURL
O script do service worker associados a essa instância. Ao usar um
TrustedScriptURL
é compatível. -
registerOptions
objeto opcional
-
retorna
Workbox (em inglês)
-
-
ativo
Promise<ServiceWorker>
-
controlando
Promise<ServiceWorker>
-
getSW
void
Resolve com uma referência a um service worker que corresponde ao URL do script da instância assim que ela estiver disponível.
Se, no momento do registro, já houver um serviço ativo ou em espera worker com um URL de script correspondente, ele será usado (com o código de espera O service worker tem precedência sobre o service worker ativo se ambos porque o service worker em espera teria sido registrado mais recentemente). Se não houver service workers ativos ou em espera correspondentes no registro a promessa não será resolvida até que uma atualização seja encontrada e iniciada da instalação, quando o service worker de instalação é usado.
A função
getSW
tem esta aparência:() => {...}
-
retorna
Promise<ServiceWorker>
-
-
messageSW
void
Envia o objeto de dados passado para o service worker registrado por este instância (via
workbox-window.Workbox#getSW
) e resolve com uma resposta (se houver).Uma resposta pode ser definida em um gerenciador de mensagens no service worker chame
event.ports[0].postMessage(...)
, o que vai resolver a promessa retornado pormessageSW()
. Se nenhuma resposta for definida, a promessa nunca resolver.A função
messageSW
tem esta aparência:(data: object) => {...}
-
dados
objeto
Objeto a ser enviado ao service worker
-
retorna
Promessa<qualquer>
-
-
messageSkipWaiting
void
Envia uma mensagem
{type: 'SKIP_WAITING'}
para o service worker que está atualmente no estadowaiting
associado ao registro atual.Se não houver um registro atual ou se nenhum service worker for
waiting
, chamar isso não terá efeito.A função
messageSkipWaiting
tem esta aparência:() => {...}
-
fazer inscrição
void
Registra um service worker para este URL de script e serviço de instâncias opções de worker. Por padrão, esse método atrasa o registro até depois a janela foi carregada.
A função
register
tem esta aparência:(options?: object) => {...}
-
opções
objeto opcional
-
próximo
booleano opcional
-
-
retorna
Promise<ServiceWorkerRegistration>
-
-
update
void
Verifica se há atualizações do service worker registrado.
A função
update
tem esta aparência:() => {...}
-
retorna
Promessa<void>
-
WorkboxEventMap
Propriedades
-
Ativado
-
ativando
-
controlando
-
instalado
-
como instalar
-
mensagem
-
redundante
-
aguardando
WorkboxLifecycleEvent
Propriedades
-
isExternal
booleano opcional
-
isUpdate
booleano opcional
-
originalEvent
Evento opcional
-
sw
ServiceWorker opcional
-
target
WorkboxEventTarget opcional
-
tipo
typeOperator
WorkboxLifecycleEventMap
Propriedades
-
Ativado
-
ativando
-
controlando
-
instalado
-
como instalar
-
redundante
-
aguardando
WorkboxLifecycleWaitingEvent
Propriedades
-
isExternal
booleano opcional
-
isUpdate
booleano opcional
-
originalEvent
Evento opcional
-
sw
ServiceWorker opcional
-
target
WorkboxEventTarget opcional
-
tipo
typeOperator
-
wasWaitingBeforeRegister
booleano opcional
WorkboxMessageEvent
Propriedades
-
dados
qualquer um
-
isExternal
booleano opcional
-
originalEvent
Evento
-
ports
typeOperator
-
sw
ServiceWorker opcional
-
target
WorkboxEventTarget opcional
-
tipo
"mensagem"
Métodos
messageSW()
workbox-window.messageSW(
sw: ServiceWorker,
data: object,
)
Envia um objeto de dados para um service worker via postMessage
e resolve com
uma resposta (se houver).
Uma resposta pode ser definida em um gerenciador de mensagens no service worker
chame event.ports[0].postMessage(...)
, o que vai resolver a promessa
retornado por messageSW()
. Se nenhuma resposta for definida, a promessa não
resolver.
Parâmetros
-
sw
ServiceWorker
O service worker para onde enviar a mensagem.
-
dados
objeto
Um objeto a ser enviado ao service worker.
Retorna
-
Promessa<qualquer>