Alguns recursos no seu aplicativo da Web podem não ser usados com frequência, muito grandes ou variam de acordo com o dispositivo (como imagens responsivas) ou o idioma do usuário. Nesses casos, o armazenamento em cache pode ser um antipadrão, e você pode usar o armazenamento em cache no ambiente de execução.
No Workbox, é possível processar o armazenamento em cache no momento da execução dos recursos usando o módulo workbox-routing
para fazer a correspondência das rotas e processar as estratégias de armazenamento em cache com o módulo workbox-strategies
.
Estratégias de armazenamento em cache
É possível processar a maioria das rotas para recursos com uma das estratégias de armazenamento em cache integradas. Eles foram abordados em detalhes anteriormente nesta documentação, mas estes são alguns que vale a pena recapitular:
- A opção Stale embora revalidar usa uma resposta armazenada em cache para uma solicitação (se estiver disponível) e atualiza o cache em segundo plano com uma resposta da rede. Portanto, se o recurso não estiver armazenado em cache, ele aguardará a resposta da rede e a usará. É uma estratégia bastante segura, porque atualiza regularmente as entradas de cache que dependem dele. A desvantagem é que ela sempre solicita um recurso da rede em segundo plano.
- Network First tenta primeiro receber uma resposta da rede. Se uma resposta for recebida, ele a transmite para o navegador e a salva em um cache. Se a solicitação de rede falhar, a última resposta armazenada em cache será usada, permitindo o acesso off-line ao recurso.
- Cache First verifica primeiro o cache em busca de uma resposta e a usa, se disponível. Se a solicitação não estiver no cache, a rede será usada e qualquer resposta válida será adicionada ao cache antes de ser transmitida ao navegador.
- Somente rede força a resposta a vir da rede.
- Somente cache força a resposta a vir do cache.
Você pode aplicar essas estratégias para selecionar solicitações usando os métodos oferecidos pela workbox-routing
.
Como aplicar estratégias de armazenamento em cache com correspondência de rota
workbox-routing
expõe um método registerRoute
para corresponder rotas e processá-las com uma estratégia de armazenamento em cache. registerRoute
aceita um objeto Route
que, por sua vez, aceita dois argumentos:
- Uma string, expressão regular ou um callback de correspondência para especificar critérios de correspondência de rota.
- Um gerenciador da rota, normalmente uma estratégia fornecida por
workbox-strategies
.
Callbacks de correspondência têm preferência para corresponder rotas porque fornecem um objeto de contexto que inclui o objeto Request
, a string do URL de solicitação, o evento de busca e um booleano que indica se a solicitação é de mesma origem.
Em seguida, o gerenciador manipula a rota correspondente. No exemplo a seguir, é criada uma nova rota que corresponde às solicitações de imagem de mesma origem que chegam, aplicando o cache primeiro, voltando à estratégia de rede.
// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
// A new route that matches same-origin image requests and handles
// them with the cache-first, falling back to network strategy:
const imageRoute = new Route(({ request, sameOrigin }) => {
return sameOrigin && request.destination === 'image'
}, new CacheFirst());
// Register the new route
registerRoute(imageRoute);
Como usar vários caches
A caixa de trabalho permite agrupar as respostas em cache em instâncias Cache
separadas usando a opção cacheName
disponível nas estratégias agrupadas.
No exemplo a seguir, as imagens usam uma estratégia "desatualizado durante a revalidação", enquanto os recursos CSS e JavaScript usam uma estratégia de rede que prioriza o cache. A rota para cada recurso coloca as respostas em caches separados, adicionando a propriedade cacheName
.
// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';
// Handle images:
const imageRoute = new Route(({ request }) => {
return request.destination === 'image'
}, new StaleWhileRevalidate({
cacheName: 'images'
}));
// Handle scripts:
const scriptsRoute = new Route(({ request }) => {
return request.destination === 'script';
}, new CacheFirst({
cacheName: 'scripts'
}));
// Handle styles:
const stylesRoute = new Route(({ request }) => {
return request.destination === 'style';
}, new CacheFirst({
cacheName: 'styles'
}));
// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);
registerRoute(stylesRoute);
![Uma captura de tela de uma lista de instâncias de Cache na guia do aplicativo do Chrome DevTools. São exibidos três caches distintos: um chamado "scripts", outro chamado "styles" e o último chamado "images".](https://developer.chrome.com/static/docs/workbox/caching-resources-during-runtime/image/a-screenshot-a-list-cac-bdd2858b485be.png?authuser=9&hl=pt)
Como definir uma expiração para as entradas de cache
Esteja ciente das cotas de armazenamento ao gerenciar os caches do service worker. ExpirationPlugin
simplifica a manutenção do cache e é exposto por workbox-expiration
. Para usá-la, especifique-a na configuração de uma estratégia de armazenamento em cache:
// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
// Evict image cache entries older thirty days:
const imageRoute = new Route(({ request }) => {
return request.destination === 'image';
}, new CacheFirst({
cacheName: 'images',
plugins: [
new ExpirationPlugin({
maxAgeSeconds: 60 * 60 * 24 * 30,
})
]
}));
// Evict the least-used script cache entries when
// the cache has more than 50 entries:
const scriptsRoute = new Route(({ request }) => {
return request.destination === 'script';
}, new CacheFirst({
cacheName: 'scripts',
plugins: [
new ExpirationPlugin({
maxEntries: 50,
})
]
}));
// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);
Obedecer às cotas de armazenamento pode ser complicado. Uma prática recomendada é considerar os usuários que estão sofrendo uma pressão de armazenamento ou querem fazer o uso mais eficiente desse espaço. Os pares de ExpirationPlugin
da caixa de trabalho podem ajudar a alcançar esse objetivo.
Considerações sobre origens diferentes
A interação entre o service worker e os recursos de origem cruzada é consideravelmente diferente do que com recursos de mesma origem. O Compartilhamento de recursos entre origens (CORS) é complicado, e essa complexidade se estende à forma como você lida com recursos entre origens em um service worker.
Respostas opacas
Ao fazer uma solicitação entre origens no modo no-cors
, a resposta pode ser armazenada em um cache de service worker e até ser usada diretamente pelo navegador. No entanto, o corpo da resposta em si não pode ser lido usando JavaScript. Isso é conhecido como resposta opaca.
Respostas opacas são uma medida de segurança destinada a evitar a inspeção de um recurso de origem cruzada. Você ainda pode fazer solicitações para recursos de origem cruzada e até armazená-los em cache, mas não é possível ler o corpo da resposta nem o código de status.
Não se esqueça de ativar o modo CORS
Mesmo que você carregue recursos de origem cruzada que definem cabeçalhos CORS permissivos que permitem a leitura de respostas, o corpo da resposta de origem cruzada ainda pode ser opaco. Por exemplo, o HTML a seguir acionará solicitações no-cors
que levarão a respostas opacas, independentemente de quais cabeçalhos do CORS estão definidos:
<link rel="stylesheet" href="https://example.com/path/to/style.css">
<img src="https://example.com/path/to/image.png">
Para acionar explicitamente uma solicitação cors
que vai produzir uma resposta não opaca, ative explicitamente o modo CORS adicionando o atributo crossorigin
ao HTML:
<link crossorigin="anonymous" rel="stylesheet" href="https://example.com/path/to/style.css">
<img crossorigin="anonymous" src="https://example.com/path/to/image.png">
É importante lembrar disso quando as rotas no service worker armazenam sub-recursos carregados no ambiente de execução.
A caixa de trabalho não pode armazenar em cache respostas opacas
Por padrão, o Workbox adota uma abordagem cautelosa para armazenar respostas opacas em cache. Como é impossível examinar o código de resposta em busca de respostas opacas, o armazenamento em cache de uma resposta de erro pode resultar em uma experiência corrompida persistente se uma estratégia que prioriza o cache ou somente o cache for usada.
Se você precisar armazenar uma resposta opaca em cache no Workbox, use uma estratégia que prioriza a rede ou obsoleto durante a validação. Sim, isso significa que o recurso ainda será solicitado pela rede todas as vezes, mas garante que as respostas com falha não permaneçam e sejam substituídas por respostas utilizáveis.
Se você usar outra estratégia de armazenamento em cache e uma resposta opaca for retornada, o Workbox avisará que a resposta não foi armazenada em cache no modo de desenvolvimento.
Forçar o armazenamento em cache de respostas opacas
Se você tiver certeza absoluta de que quer armazenar em cache uma resposta opaca usando uma estratégia que prioriza o cache ou apenas o cache, poderá forçar o Workbox a fazer isso com o módulo workbox-cacheable-response
:
import {Route, registerRoute} from 'workbox-routing';
import {NetworkFirst, StaleWhileRevalidate} from 'workbox-strategies';
import {CacheableResponsePlugin} from 'workbox-cacheable-response';
const cdnRoute = new Route(({url}) => {
return url === 'https://cdn.google.com/example-script.min.js';
}, new CacheFirst({
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200]
})
]
}))
registerRoute(cdnRoute);
Respostas opacas e a API navigator.storage
Para evitar o vazamento de informações entre domínios, foi adicionado padding significativo ao tamanho de uma resposta opaca usada para calcular os limites da cota de armazenamento. Isso afeta a forma como a API navigator.storage
informa as cotas de armazenamento.
Esse preenchimento varia de acordo com o navegador. No entanto, para o Chrome, o tamanho mínimo que qualquer resposta opaca em cache única contribui para o armazenamento geral usado é aproximadamente 7 megabytes. Lembre-se disso ao determinar quantas respostas opacas você quer armazenar em cache, já que é possível exceder as cotas de armazenamento muito antes do esperado.