Lidando com o cache do navegador em aplicativos de página única


27

Estou tentando descobrir como lidar adequadamente com o cache do navegador da Web para aplicativos de página única.

Eu tenho um design bastante típico: vários arquivos HTML, JS e CSS implementando o SPA e um monte de dados JSON consumidos pelo SPA. Os problemas surgem quando desejo enviar uma atualização: atualizo a parte estática do site e o código que gera o JSON ao mesmo tempo, mas os navegadores clientes geralmente têm a parte estática em cache, portanto, o código antigo tenta processar os novos dados e pode (dependendo das alterações feitas) ter problemas. (Em particular, o IE parece mais agressivo do que o Chrome ou o Firefox sobre o uso de JS em cache sem revalidar.)

Qual é a melhor maneira de lidar com isso?

  1. Verifique se minhas alterações no JSON são compatíveis com versões anteriores e assuma que os caches do navegador expirarão em um prazo razoável.
  2. Incorpore algum tipo de número de versão no JS estático e no JSON e execute window.location.reload(true);se não corresponderem.
  3. Descubra a combinação apropriada de cabeçalhos ( must-revalidateou no-cachequalquer outra coisa; as fontes variam de como fazer isso) para garantir que os navegadores sempre revalidem todos os recursos em cada carregamento, mesmo que isso signifique algumas viagens extras para carregar o site.
  4. Microgerencie meu controle de cache e expire os cabeçalhos para que o conteúdo estático expire quando eu desejar enviar uma atualização.
  5. Algo mais?

1
Você já ouviu os números 3 e 4 falharem inesperadamente nos controles do navegador da Web incorporado se o seu ambiente for ruim ( iOS tosse ) dos colegas de trabalho. Os itens 1 e 2 podem ser uma opção no nível do aplicativo, mas ainda podem (?) Causar problemas de cache para outros recursos ou para cargas parciais de recursos. A única coisa que eu vi trabalhar de forma confiável no código pronto para produção é buscar o yoururl.html? <SomeTimeStamp>, pois isso falsificará a maioria dos mecanismos de cache. Bônus: role todo o aplicativo da web em um arquivo para que o carregamento seja bem-sucedido ou falhado atomicamente. Desvantagem: funciona melhor em um link local. Sua milhagem pode variar. Boa sorte!
J Trana

2
+1 para usar um número de versão ou um carimbo de data e hora como parâmetro de URL para recursos.
9000

Respostas:


14

Você precisa de uma solução de bloqueio de cache . O papel do bloqueio de cache é:

  1. Renomeie os recursos para um nome exclusivo, dependendo do conteúdo.
  2. Atualize todas as referências a esses recursos.

Em um projeto baseado no Grunt, é comum usar o grunt-rev para garantir que todos os arquivos que precisam ser atualizados tenham nomes exclusivos, com base em seu conteúdo.

Se você garantir que seus arquivos JSON recebam nomes de arquivos com armazenamento em cache, juntamente com as referências a eles em seu Javascript, os clientes sempre carregarão os arquivos JSON que o Javascript espera.

A vantagem da nomeação de arquivo baseada em hash é que os arquivos que não foram alterados terão os mesmos nomes de arquivo após a interrupção do cache, para que os navegadores possam continuar a usar com segurança o conteúdo em cache quando ele não for alterado.

Obviamente, esse é o tipo de coisa que você deseja automatizar como parte da produção do seu projeto, para que você não precise acompanhar manualmente a alteração dos nomes e referências de arquivos.


2
+1 para o bit "cache busting" em itálico, que abre a porta para realmente pesquisar essas coisas no Google de maneira produtiva.
Zak Kus 12/12

@ Perced Ted - estrutura yeoman faz isso, que eu uso, mas estou vendo um problema. quando ligo uma nova compilação, o navegador pode ter o index.html em cache com referências aos arquivos antigos ... e o navegador recebe um erro. como devo corrigir isso? (A.) vincule todos os nomes de arquivos antigos aos novos (isso funciona) (B.) adicione cabeçalho sem cache ao index.html (mas isso sempre é respeitado) (C.) adicione .htaccess para reconhecer um arquivo aprimorado e pesquise a base (por exemplo, 12345.main.js -> main.js)
timh

5

Você pode usar if-modified-since + last-modifiedou if-none-match + etagcabeçalhos junto com o cache-controlcabeçalho apropriado . (Pode haver erros no navegador , mas nada que você não possa gerenciar nos navegadores recentes.)

Se os arquivos forem estáticos, sugiro que você use if-modified-since, porque isso pode ser feito automaticamente com um servidor HTTP bem configurado. Ele deve enviar 304 se o arquivo não for modificado desde o último download.

Eu não acho que o seu número 1 e o número 2 funcionariam a longo prazo. O nº 3 ou nº 4 pode funcionar. O nº 3 é mais simples, mas você precisa aprender a lidar com esse problema apenas uma vez. Então, eu tentaria o nº 4 se fosse você, mas a solução pode depender de quais navegadores seus clientes usam ... Por exemplo, o IE8 tem problemas ao atualizar o cache ajax, etc ...


2

Se você pode incluir o Java Servlet Filter no seu SPA, aqui está uma solução funcional: CorrectBrowserCacheHandlerFilter.java

Basicamente, quando o navegador solicita os arquivos estáticos, o servidor redireciona todas as solicitações para o mesmo, mas com um parâmetro de consulta de hash ( ?v=azErTpor exemplo), que depende do conteúdo do arquivo estático de destino.

Fazendo isso, o navegador nunca armazenará em cache os arquivos estáticos declarados no seu, index.htmlpor exemplo (porque sempre receberá um 302 Moved Temporarily), mas armazenará apenas em cache os arquivos com a versão hash (o servidor responderá 200por eles). Portanto, o cache do navegador será usado com eficiência para esses arquivos estáticos com a versão hash.

Disclaimer: Eu sou o autor de CorrectBrowserCacheHandlerFilter.java.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.