Como forçar o navegador a recarregar arquivos CSS / JS em cache?


993

Percebi que alguns navegadores (em particular, Firefox e Opera) são muito zelosos ao usar cópias em cache de arquivos .css e .js , mesmo entre as sessões do navegador. Isso causa um problema quando você atualiza um desses arquivos, mas o navegador do usuário continua usando a cópia em cache.

A questão é: qual é a maneira mais elegante de forçar o navegador do usuário a recarregar o arquivo quando ele foi alterado?

Idealmente, a solução não forçaria o navegador a recarregar o arquivo em todas as visitas à página. Vou postar minha própria solução como resposta, mas estou curioso para saber se alguém tem uma solução melhor e deixarei seus votos decidirem.

Atualização:

Depois de permitir a discussão aqui por um tempo, achei a sugestão de John Millikin e da5id útil. Acontece que existe um termo para isso: versão automática .

Publiquei uma nova resposta abaixo, que é uma combinação da minha solução original e da sugestão de John.

Outra idéia sugerida pelo SCdF seria anexar uma string de consulta falsa ao arquivo. (Algum código Python para usar automaticamente o registro de data e hora como uma string de consulta falsa foi enviado por pi .). No entanto, há alguma discussão sobre se o navegador armazenará ou não um arquivo em cache com uma string de consulta. (Lembre-se de que queremos que o navegador armazene em cache o arquivo e use-o em futuras visitas. Queremos que apenas busque o arquivo novamente quando ele tiver sido alterado.)

Como não está claro o que acontece com uma string de consulta falsa, não estou aceitando essa resposta.


Eu tenho isso no meu .htaccess, e nunca qualquer problema com arquivos em cache: ExpiresActive On ExpiresDefault "modification".
Frank Conijn

2
Definitivamente, eu concordo que adicionar informações de versão ao URL do arquivo é de longe o melhor caminho a percorrer. Funciona, o tempo todo, para todos. Mas, se você não o estiver usando, e você apenas precisa recarregar esse arquivo CSS ou JS ocasionalmente em seu próprio navegador ... basta abri-lo em sua própria guia e pressionar SHIFT-reload (ou CTRL-F5)! Você pode efetivamente fazer a mesma coisa usando JS, carregando um arquivo em um iframe (oculto), aguardando o carregamento e, em seguida, chamando iframe.contentWindow.location.reload(true). Veja o método (4) de stackoverflow.com/a/22429796/999120 - trata-se de imagens, mas o mesmo se aplica.
Doin

2
Eu realmente aprecio a maneira como essa pergunta foi feita e foi atualizada desde então. Descreveu completamente o que eu deveria esperar nas respostas. Vou seguir essa abordagem nas minhas perguntas a partir de agora. Felicidades!
Rd22

Respostas:


455

Atualização: reescrita para incorporar sugestões de John Millikin e da5id . Esta solução é escrita em PHP, mas deve ser facilmente adaptada a outras linguagens.

Atualização 2: Incorporando comentários de Nick Johnson de que o .htaccessregex original pode causar problemas com arquivos como json-1.3.js. A solução é reescrever apenas se houver exatamente 10 dígitos no final. (Como 10 dígitos abrange todos os carimbos de data e hora de 9/9/2001 a 20/11/2286.)

Primeiro, usamos a seguinte regra de reescrita em .htaccess:

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]

Agora, escrevemos a seguinte função PHP:

/**
 *  Given a file, i.e. /css/base.css, replaces it with a string containing the
 *  file's mtime, i.e. /css/base.1221534296.css.
 *  
 *  @param $file  The file to be loaded.  Must be an absolute path (i.e.
 *                starting with slash).
 */
function auto_version($file)
{
  if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
    return $file;

  $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
  return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}

Agora, onde quer que você inclua seu CSS, altere-o a partir deste:

<link rel="stylesheet" href="/css/base.css" type="text/css" />

Para isso:

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />

Dessa forma, você nunca precisará modificar a tag do link novamente, e o usuário sempre verá o CSS mais recente. O navegador poderá armazenar em cache o arquivo CSS, mas quando você fizer alterações no CSS, o navegador o verá como um novo URL, portanto, não usará a cópia em cache.

Isso também pode funcionar com imagens, ícones favoritos e JavaScript. Basicamente, qualquer coisa que não seja gerada dinamicamente.


16
Meu próprio servidor de conteúdo estático faz exatamente o mesmo, exceto que eu uso um parâmetro para controle de versão (base.css? V = 1221534296) em vez de uma alteração no nome do arquivo (base.1221534296.css). Eu suspeito que seu caminho possa ser um pouco mais eficiente. Muito legal.
Jens Roland #

4
@ Kip: Solução muito lisa. A reescrita de URL obviamente tem muito mais a oferecer do que apenas urls bonitos.
James P.

37
Eu vejo um problema com isso, que acessa o sistema de arquivos muitas vezes - exatamente - número de links * número de solicitações / s ... que pode ou não ser um problema para você.
Tomáš Fejfar

3
@AlixAxel: Não, os navegadores o buscarão novamente quando o parâmetro for alterado, mas alguns proxies públicos não armazenarão em cache arquivos com parâmetros de URL, portanto, a melhor prática é incluir a versão no caminho. E a sobrecarga mod_rewrite é minúscula em comparação com todos os outros gargalos de desempenho no WPO
Jens Roland

8
O primeiro file_existscheque é realmente necessário? filemtimeretornará false em caso de falha; por que não atribuir o valor do tempo de filem a uma variável e verificar se é falso antes de renomear o arquivo? Isso reduziria uma operação desnecessária de arquivo que realmente aumentaria.
Gavin

184

Técnica simples do lado do cliente

Em geral, o armazenamento em cache é bom. Portanto, existem algumas técnicas, dependendo se você está solucionando o problema por conta própria ao desenvolver um site ou se está tentando controlar o cache em um ambiente de produção.

Os visitantes gerais do seu site não terão a mesma experiência que você está tendo ao desenvolver o site. Como o visitante médio acessa o site com menos frequência (talvez apenas algumas vezes por mês, a menos que você seja um Google ou hi5 Networks), é menos provável que eles tenham seus arquivos em cache, e isso pode ser suficiente. Se você deseja forçar uma nova versão no navegador, sempre pode adicionar uma sequência de consultas à solicitação e aumentar o número da versão ao fazer grandes alterações:

<script src="/myJavascript.js?version=4"></script>

Isso garantirá que todos obtenham o novo arquivo. Funciona porque o navegador verifica a URL do arquivo para determinar se ele possui uma cópia no cache. Se o servidor não estiver configurado para fazer qualquer coisa com a sequência de consultas, ele será ignorado, mas o nome parecerá um novo arquivo para o navegador.

Por outro lado, se você estiver desenvolvendo um site, não deseja alterar o número da versão sempre que salvar uma alteração na sua versão de desenvolvimento. Isso seria entediante.

Portanto, enquanto você estiver desenvolvendo seu site, um bom truque seria gerar automaticamente um parâmetro de string de consulta:

<!-- Development version: -->
<script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script>

Adicionar uma sequência de consultas à solicitação é uma boa maneira de versão de um recurso, mas para um site simples, isso pode ser desnecessário. E lembre-se, o cache é uma coisa boa.

Também é importante notar que o navegador não é necessariamente mesquinho para manter os arquivos em cache. Os navegadores têm políticas para esse tipo de coisa e geralmente estão seguindo as regras estabelecidas na especificação HTTP. Quando um navegador faz uma solicitação a um servidor, parte da resposta é um cabeçalho EXPIRES. Uma data que informa ao navegador quanto tempo deve ser mantido em cache. Na próxima vez em que o navegador encontrar uma solicitação para o mesmo arquivo, ele verá que possui uma cópia no cache e procurará a data EXPIRES para decidir se deve ser usado.

Então, acredite ou não, na verdade, é o seu servidor que está tornando o cache do navegador tão persistente. Você pode ajustar as configurações do servidor e alterar os cabeçalhos EXPIRES, mas a pequena técnica que escrevi acima é provavelmente uma maneira muito mais simples de você fazer isso. Como o armazenamento em cache é bom, geralmente você deseja definir essa data no futuro (um "Cabeçalho de expiração do futuro distante") e usar a técnica descrita acima para forçar uma alteração.

Se você estiver interessado em obter mais informações sobre HTTP ou sobre como essas solicitações são feitas, um bom livro é "Sites de alto desempenho", de Steve Souders. É uma introdução muito boa ao assunto.


3
O truque rápido de gerar uma string de consulta com Javascript funciona muito bem durante o desenvolvimento ativo. Eu fiz a mesma coisa com PHP.
Alan Turing

2
Essa é a maneira mais fácil de obter o resultado desejado do pôster original. O método mod_rewrite funciona bem se você deseja forçar um recarregamento do arquivo .css ou .js Toda vez que você carrega a página. Esse método ainda permite o armazenamento em cache até que você realmente altere o arquivo e realmente queira que ele seja forçado a recarregar.
scott80109

@keparo, eu tenho um amplo número de jquery em todas as páginas, se eu vou mudar isso manualmente, levará um mês. Se você pode me ajudar a resolver tudo sem codificar para cada página.
Crack26

1
Isso não parece funcionar para o meu CSS quando eu uso:<link href='myCss.css?dev=14141'...>
Noumenon

3
Esta não é uma solução viável. Um bom número de navegadores simplesmente se recusará a armazenar em cache qualquer coisa com uma string de consulta. Essa é a razão pela qual o Google, o GTMetrix e ferramentas semelhantes irão exibir uma bandeira se você tiver cadeias de consulta em referências a conteúdo estático. Embora seja certamente uma solução decente para o desenvolvimento, não é absolutamente uma solução para a produção. Além disso, o navegador controla o cache, não o servidor. O servidor simplesmente SUGERE quando deve ser atualizado; um navegador não precisa ouvir o servidor (e muitas vezes não). Os dispositivos móveis são um excelente exemplo disso.
Nate I

113

O plug- in mod_pagespeed do Google para apache fará a versão automática para você. É realmente liso.

Ele analisa o HTML ao sair do servidor da web (funciona com PHP, rails, python, HTML estático - qualquer coisa) e reescreve links para arquivos de imagem CSS, JS, para que incluam um código de identificação. Ele serve os arquivos nos URLs modificados com um controle de cache muito longo. Quando os arquivos mudam, os URLs são alterados automaticamente para que o navegador os busque novamente. Basicamente, funciona, sem nenhuma alteração no seu código. Ele também reduzirá seu código na saída.


1
Isso é ótimo, mas ainda está na versão beta. Pode ser usado para serviços corporativos?
Sanghyun Lee 15/07

26
Isso é ERRADO (manipulação automática da fonte) quando é claramente um problema do navegador. Dê a nós (desenvolvedores) uma atualização de limpeza cerebral real: <ctrl> + F5
T4NK3R

25
mod_pagespeed é funcionalmente equivalente a uma etapa de compilação / compilação totalmente automática para o seu html / css / js. Eu acho que seria difícil encontrar desenvolvedores sérios que pensem que os sistemas de construção estão intrinsecamente errados ou que há algo errado com isso ser completamente automático. A analogia de uma compilação limpa é limpar o cache do mod_pagespeed : code.google.com/p/modpagespeed/wiki/… ?
Leopd

3
O mod_pagespeed do T4NK3R não precisa fazer nada com a sua fonte para gerenciar o cache, foi simplesmente mencionado que ele pode ajudar com coisas como minificação. Quanto a saber se é "ERRADO" ou não, é completamente subjetivo. Pode ser errado para você, mas isso não significa que é instirinsicamente ruim .
Madbreaks

2
Também funciona com o nginx, embora você precise construí-lo a partir da fonte: developers.google.com/speed/pagespeed/module/…
Rohit

93

Em vez de alterar a versão manualmente, recomendo que você use um hash MD5 do arquivo CSS real.

Portanto, seu URL seria algo como

http://mysite.com/css/[md5_hash_here]/style.css

Você ainda pode usar a regra de reescrita para remover o hash, mas a vantagem é que agora você pode definir sua política de cache como "cache para sempre", pois se a URL for a mesma, isso significa que o arquivo permanece inalterado.

Em seguida, você pode escrever um script de shell simples que calcule o hash do arquivo e atualize sua tag (você provavelmente deseja movê-lo para um arquivo separado para inclusão).

Basta executar esse script toda vez que o CSS mudar e você for bom. O navegador apenas recarregará seus arquivos quando eles forem alterados. Se você fizer uma edição e depois desfazê-la, não há problema em descobrir em qual versão você precisa retornar para que seus visitantes não façam o download novamente.


1
infelizmente não sei como implementá-lo. Conselho por favor ... mais detalhes ...
Michael Phelps

Uma implementação com casca, rubi, etc seria ótimo
Peter

3
Solução muito boa .. mas acho que consome recursos para calcular o hash do arquivo em cada solicitação de arquivo (css, js, imagens, html..etc) para cada visita à página.
DeepBlue

Esta é uma solução padrão para aqueles que utilizam js ou css agregação com gole, grunhido ou Webpack, os difere de implementação para cada solução, mas hash seus arquivos como uma etapa de compilação é comum e sugeriu para aplicativos empacotados modernos
Brandon Søren Culley

@DeepBlue - a resposta diz "execute esse script sempre que o CSS mudar" . Isso NÃO está em todas as visitas à página. OTOH A resposta deixa de fora os principais detalhes - como o hash alterado se torna parte do URL? Eu não sei ...
ToolmakerSteve

71

Não sei por que vocês estão sofrendo tanto para implementar esta solução.

Tudo o que você precisa fazer se obter o carimbo de data / hora modificado do arquivo e anexá-lo como uma string de consulta ao arquivo

No PHP, eu faria isso como:

<link href="mycss.css?v=<?= filemtime('mycss.css') ?>" rel="stylesheet">

filemtime é uma função PHP que retorna o carimbo de data / hora modificado do arquivo.


Você pode apenas usar mycss.css?1234567890.
Gavin

3
muito elegante, apesar de eu ter um pouco modificado para <link rel="stylesheet" href="mycss.css?<?php echo filemtime('mycss.css') ?>"/>, no caso de alguns dos argumentos sobre este tópico sobre o cache URL de com variáveis GET (no formato sugerido) estão corretas
luke_mclachlan

depois do meu último comentário, vi que o wordpress usa ?ver=quem sabe!
luke_mclachlan

Ótima solução. Além disso, descobri que o tempo de filem não funcionava para um nome de domínio totalmente qualificado (FQDN), então usei o FQDN para a parte href e $ _SERVER ["DOCUMENT_ROOT"] para a parte do tempo de filem. EX: <link rel = "folha de estilos" href = "http: //theurl/mycss.css? V = <? Php echo filemtime ($ _ SERVER [" DOCUMENT_ROOT "]. '/Mycss.css')?>" />
Rrtx2000

Muito obrigado. Simples e bom. Aqui está em Python: progpath = os.path.dirname (sys.argv [0]) def versionize (arquivo): timestamp = os.path.getmtime ('% s /../ web /% s'% (progpath , arquivo)) retorne '% s? v =% s'% (arquivo, carimbo de data / hora) print <link href = "% s" rel = "folha de estilo" '' type = "text / css" /> '\% versionize ( 'css / main.css')
dlink

52

Você pode simplesmente colocar ?foo=1234no final de sua importação de css / js, alterando 1234 para o que quiser. Dê uma olhada na fonte SO html para um exemplo.

A idéia de que é isso? de qualquer maneira, os parâmetros são descartados / ignorados na solicitação e você pode alterar esse número ao lançar uma nova versão.


Nota: Há algum argumento sobre exatamente como isso afeta o armazenamento em cache. Eu acredito que a essência geral disso é que as solicitações GET, com ou sem parâmetros, devem ser alteráveis, portanto a solução acima deve funcionar.

No entanto, cabe ao servidor da web decidir se deseja aderir à parte da especificação e ao navegador que o usuário usa, pois ele pode seguir em frente e solicitar uma versão nova de qualquer maneira.


Absurdo. A string de consulta (também conhecida como parâmetros GET) faz parte da URL. Eles podem e serão armazenados em cache. Esta é uma boa solução.
troelskn 23/09/08

9
@troelskn: A especificação HTTP 1.1 diz o contrário (com relação às solicitações GET e HEAD com parâmetros de consulta): os caches NÃO DEVEM tratar as respostas a esses URIs como frescas, a menos que o servidor forneça um prazo de validade explícito. Consulte w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9
Michael Johnson

4
Eu tentei o tipo de versão de string de consulta com todos os principais navegadores e eles fazem o cache do arquivo, com ou sem especificações. No entanto, acho melhor usar o formato style.TIMESTAMP.css sem abusar das cadeias de consulta, porque ainda existe a possibilidade de o cache do software proxy NÃO armazenar em cache o arquivo.
Tomas Andrle 08/10/09

34
Vale a pena notar, por qualquer motivo, que o Stackoverflow em si usa o método de string de consulta.
jason

2
Verificaram que o uso do parâmetro? = Não fará com que os navegadores busquem novamente o arquivo em cache quando o parâmetro for alterado. A única maneira é mudar o próprio nome do arquivo programaticamente no final servidor que respondeu por Kip
arunskrish

41

Eu ouvi isso chamado "versionamento automático". O método mais comum é incluir o mtime do arquivo estático em algum lugar da URL e removê-lo usando manipuladores de reescrita ou confs de URL:

Veja também:


3
Obrigado, acho que esse foi outro caso em que minha ideia foi discutida; eu simplesmente não sabia como era chamada, então nunca a encontrei nas pesquisas do Google.
Kip

27

As cerca de 30 respostas existentes são ótimos conselhos para um site de cerca de 2008. No entanto, quando se trata de um aplicativo moderno de página única (SPA), talvez seja hora de repensar algumas suposições fundamentais ... especificamente a idéia de que é desejável que o servidor da Web sirva apenas a versão única e mais recente de um Arquivo.

Imagine que você é um usuário que possui a versão M de um SPA carregada no seu navegador:

  1. Seu pipeline de CD implementa a nova versão N do aplicativo no servidor
  2. Você navega dentro do SPA, que envia um XHR ao servidor para obter /some.template
    • (Seu navegador não atualizou a página, você ainda está executando a versão M )
  3. O servidor responde com o conteúdo de /some.template- você deseja que ele retorne a versão M ou N do modelo?

Se o formato /some.templatemudou entre as versões M e N (ou o arquivo foi renomeado ou o que for), você provavelmente não deseja que a versão N do modelo seja enviada ao navegador que está executando a versão antiga M do analisador . †

Os aplicativos da Web enfrentam esse problema quando duas condições são atendidas:

  • Os recursos são solicitados de forma assíncrona em algum momento após o carregamento inicial da página
  • A lógica do aplicativo pressupõe coisas (que podem mudar em versões futuras) sobre o conteúdo do recurso

Depois que o aplicativo precisar exibir várias versões em paralelo, a solução do cache e do "recarregamento" se tornará trivial:

  1. Instale todos os arquivos do site em diretórios versionados: /v<release_tag_1>/…files…,/v<release_tag_2>/…files…
  2. Defina cabeçalhos HTTP para permitir que os navegadores armazenem em cache os arquivos para sempre
    • (Ou melhor ainda, coloque tudo em uma CDN)
  3. Atualizar todos <script>e <link>etiquetas, etc. para apontar para esse arquivo em um dos diretórios versionados

Essa última etapa parece complicada, pois pode exigir a chamada de um construtor de URL para cada URL no código do servidor ou do cliente. Ou você pode fazer um uso inteligente da <base>tag e alterar a versão atual em um só lugar.

† Uma maneira de contornar isso é ser agressivo ao forçar o navegador a recarregar tudo quando uma nova versão for lançada. Porém, para permitir a conclusão de qualquer operação em andamento, ainda pode ser mais fácil oferecer suporte a pelo menos duas versões em paralelo: v-current e v-previous.


Michael - seu comentário é muito relevante. Eu cam aqui precisamente tentando encontrar uma solução para o meu SPA. Eu recebi algumas dicas, mas tive que encontrar uma solução. No final, fiquei muito feliz com o que descobri, por isso escrevi uma postagem no blog e uma resposta a essa pergunta (incluindo código). Obrigado pela ponteiros
statler

Ótimo comentário. Não consigo entender enquanto as pessoas continuam falando sobre o bloqueio de cache e o cache HTTP como a solução real para problemas de cache de sites sem comentar os novos problemas dos SPAs, como se esse fosse um caso marginal.
David Casillas

1
Excelente resposta e estratégia absolutamente ideal! E pontos de bônus por mencionar a basetag! Quanto ao suporte ao código antigo: isso nem sempre é uma possibilidade, nem sempre é uma boa ideia. Novas versões de código podem oferecer suporte à quebra de alterações em outras partes de um aplicativo ou podem envolver correções de emergência, patches de vulnerabilidade etc. Eu ainda não implementei essa estratégia, mas sempre achei que a arquitetura geral deveria permitir que as implantações etiquetassem uma versão antiga obsoletee forçassem um recarregamento na próxima vez que uma chamada assíncrona for feita (ou apenas desautorize forçosamente todas as sessões via WebSockets )
Jonny Asmar

É bom ver uma resposta bem pensada em relação a aplicativos de página única.
Nate I

Essa é a "implantação azul esverdeada" se você deseja procurar mais informações.
Fil

15

Não use foo.css? Version = 1! Os navegadores não devem armazenar em cache URLs com variáveis ​​GET. De acordo com http://www.thinkvitamin.com/features/webapps/serving-javascript-fast , embora o IE e o Firefox ignorem isso, o Opera e o Safari não! Em vez disso, use foo.v1234.css e use regras de reescrita para remover o número da versão.


1
Antes de tudo, os navegadores não fazem cache, isso é uma função do HTTP. Por que o http se preocupa com a estrutura de um URI? Existe uma referência oficial a uma especificação que declara que o cache HTTP deve entender a semântica de um URI para que ele não armazene em cache itens com uma string de consulta?
AnthonyWJones

13
Um navegador da web que inclui a funcionalidade de objetos de armazenamento em cache (verifique o diretório de cache do seu navegador). O HTTP é um protocolo que inclui diretivas de servidores para clientes (proxies, navegadores, spiders etc.), sugerindo controle de cache.
tzot 29/09/08

13

No Laravel (PHP), podemos fazê-lo da seguinte maneira clara e elegante (usando o timestamp de modificação de arquivo):

<script src="{{ asset('/js/your.js?v='.filemtime('js/your.js')) }}"></script>

E semelhante para CSS

<link rel="stylesheet" href="{{asset('css/your.css?v='.filemtime('css/your.css'))}}">

Exemplo de saída html ( filemtimetempo de retorno como carimbo de data e hora do Unix )

<link rel="stylesheet" href="assets/css/your.css?v=1577772366">

qual é a saída desse comando em html? E se eu precisar renovar apenas versões como v = 3, v = 4 e etc.? - não força navegador para usuários carga css toda vez que entra no website
Gediminas

filemtime : "Esta função retorna a hora em que os blocos de dados de um arquivo estavam sendo gravados, ou seja, a hora em que o conteúdo do arquivo foi alterado." src: php.net/manual/pt/function.filemtime.php
Kamil Kiełczewski

11

O RewriteRule precisa de uma pequena atualização para arquivos js ou css que contêm uma versão de notação de ponto no final. Por exemplo, json-1.3.js.

Adicionei uma classe de negação de ponto [^.] Ao regex, para .number. é ignorado.

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]

2
Obrigado pela contribuição! Desde que escrevi este post, eu também fui queimado por isso. Minha solução foi reescrever apenas se a última parte do nome do arquivo contiver exatamente dez dígitos. (10 dígitos abrangem todos os registros de data e hora de 9/9/2001 a 20/11/2286.) Atualizei minha resposta para incluir este regex:^(.*)\.[\d]{10}\.(css|js)$ $1.$2
Kip

Entendo regex, mas não entendo com que problema você está resolvendo [^.]aqui. Além disso, não há nenhum benefício em escrever \ddentro de uma classe de personagem - \d+fará a mesma coisa. Conforme publicado, seu padrão corresponderá a qualquer número de caracteres (avidamente), depois a um ponto literal, depois a um não-ponto, a um ou mais dígitos, a um ponto, a cssou jsentão ao final do nome do arquivo. Nenhuma correspondência para sua entrada de amostra: regex101.com/r/RPGC62/1
mickmackusa

10

Para o ASP.NET 4.5 e superior, você pode usar o agrupamento de scripts .

A solicitação http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81é para o pacote configurável AllMyScripts e contém um par de cadeias de consulta v = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81. A sequência de consultas v possui um token de valor que é um identificador exclusivo usado para armazenamento em cache. Desde que o pacote não seja alterado, o aplicativo ASP.NET solicitará o pacote AllMyScripts usando esse token. Se algum arquivo do pacote for alterado, a estrutura de otimização do ASP.NET gerará um novo token, garantindo que as solicitações do navegador para o pacote tenham o pacote mais recente.

Existem outros benefícios no pacote, incluindo desempenho aprimorado em carregamentos de primeira página com minificação.


Por favor, ajude-me. Não estou fazendo nenhuma alteração no bundle.config, apenas alterando os arquivos css ou js. Como posso resolver o problema de armazenamento em cache?
vedankita Kumbhar

10

Aqui está uma solução JavaScript pura

(function(){

    // Match this timestamp with the release of your code
    var lastVersioning = Date.UTC(2014, 11, 20, 2, 15, 10);

    var lastCacheDateTime = localStorage.getItem('lastCacheDatetime');

    if(lastCacheDateTime){
        if(lastVersioning > lastCacheDateTime){
            var reload = true;
        }
    }

    localStorage.setItem('lastCacheDatetime', Date.now());

    if(reload){
        location.reload(true);
    }

})();

O item acima procurará a última vez que o usuário visitou seu site. Se a última visita foi antes do lançamento do novo código, ele será usado location.reload(true)para forçar a atualização da página do servidor.

Normalmente, eu tenho esse como o primeiro script dentro <head>dele, por isso é avaliado antes que qualquer outro conteúdo seja carregado. Se uma recarga precisar ocorrer, dificilmente será percebida pelo usuário.

Estou usando o armazenamento local para armazenar o carimbo de data e hora da última visita no navegador, mas você pode adicionar cookies à mistura se desejar oferecer suporte a versões mais antigas do IE.


Eu tentei algo assim, isso só funcionará na página recarregada, mas se o site tiver várias páginas compartilhando o mesmo css / imagens, outras páginas ainda usarão recursos antigos.
DeepBlue

9

Postagem interessante. Depois de ler todas as respostas aqui combinadas com o fato de que nunca tive problemas com cadeias de consulta "falsas" (o que não sei por que todos relutam em usar isso), acho que a solução (que elimina a necessidade de regras de reescrita do apache como na resposta aceita) é calcular um HASH curto do conteúdo do arquivo CSS (em vez da data e hora do arquivo) como uma string de consulta falsa.

Isso resultaria no seguinte:

<link rel="stylesheet" href="/css/base.css?[hash-here]" type="text/css" />

É claro que as soluções datetime também fazem o trabalho no caso de editar um arquivo CSS, mas acho que é sobre o conteúdo do arquivo css e não sobre o datetime do arquivo, então por que misturá-los?


8

Para o meu desenvolvimento, acho que o chrome tem uma ótima solução.

https://developer.chrome.com/devtools/docs/tips-and-tricks#hard-reload

Com as ferramentas do desenvolvedor abertas, basta clicar e clicar no botão Atualizar e soltar quando passar o mouse sobre "Esvaziar o cache e recarregar".

Este é meu melhor amigo e é uma maneira super leve de conseguir o que você quer!


E se você estiver usando o Chrome como seu ambiente de desenvolvimento, outra solução não invasiva é desativar o cache: na engrenagem Configurações, você pode invalidar o cache do disco selecionando 'Desativar cache' (observação: o DevTools deve estar visível / aberto para que isso funcione).
Velojet

7

Agradecemos a Kip por sua solução perfeita!

Eu o estendi para usá-lo como um Zend_view_Helper. Como meu cliente executou sua página em um host virtual, eu também a estendi para isso.

Espero que ajude outra pessoa também.

/**
 * Extend filepath with timestamp to force browser to
 * automatically refresh them if they are updated
 *
 * This is based on Kip's version, but now
 * also works on virtual hosts
 * @link http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files
 *
 * Usage:
 * - extend your .htaccess file with
 * # Route for My_View_Helper_AutoRefreshRewriter
 * # which extends files with there timestamp so if these
 * # are updated a automatic refresh should occur
 * # RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
 * - then use it in your view script like
 * $this->headLink()->appendStylesheet( $this->autoRefreshRewriter($this->cssPath . 'default.css'));
 *
 */
class My_View_Helper_AutoRefreshRewriter extends Zend_View_Helper_Abstract {

    public function autoRefreshRewriter($filePath) {

        if (strpos($filePath, '/') !== 0) {

            // path has no leading '/'
            return $filePath;
        } elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . $filePath)) {

            // file exists under normal path
            // so build path based on this
            $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $filePath);
            return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
        } else {

            // fetch directory of index.php file (file from all others are included)
            // and get only the directory
            $indexFilePath = dirname(current(get_included_files()));

            // check if file exist relativ to index file
            if (file_exists($indexFilePath . $filePath)) {

                // get timestamp based on this relativ path
                $mtime = filemtime($indexFilePath . $filePath);

                // write generated timestamp to path
                // but use old path not the relativ one
                return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
            } else {

                return $filePath;
            }
        }
    }

}

Saúde e obrigado.


7

Não encontrou a abordagem DOM do lado do cliente criando o elemento nó do script (ou css) dinamicamente:

<script>
    var node = document.createElement("script"); 
    node.type = "text/javascript";
    node.src = 'test.js?'+Math.floor(Math.random()*999999999);
    document.getElementsByTagName("head")[0].appendChild(node);
</script>

6

o google chrome tem a opção Recarregar com disco rígido , bem como a opção Cache vazio e Recarregar disco rígido. Você pode clicar e segurar o botão recarregar (no modo de inspeção) para selecionar um.


Para esclarecer, por "Inspecionar Mode", eles estão se referindo a "Dev Tools" aka F12, aka ctrl + shift + i, aka ant menu> More Tools> Developer Tools, aka right click> Inspect Element. Há também uma configuração escondida em algum lugar nas ferramentas de desenvolvimento (eu esqueço a localização) para recarregar com força a cada recarga.
Jonny Asmar

5

Você pode forçar um "cache em toda a sessão" se adicionar o ID da sessão como um parâmetro espúrio do arquivo js / css:

<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>

Se você deseja um armazenamento em cache em toda a versão, adicione um código para imprimir a data do arquivo ou similar. Se você estiver usando Java, poderá usar uma tag personalizada para gerar o link de uma maneira elegante.

<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>

5

Digamos que você tenha um arquivo disponível em:

/styles/screen.css

você pode anexar um parâmetro de consulta com informações de versão no URI, por exemplo:

/styles/screen.css?v=1234

ou você pode anexar informações da versão, por exemplo:

/v/1234/styles/screen.css

IMHO, o segundo método é melhor para arquivos CSS, pois eles podem se referir a imagens usando URLs relativos, o que significa que, se você especificar background-imageo seguinte:

body {
    background-image: url('images/happy.gif');
}

seu URL será efetivamente:

/v/1234/styles/images/happy.gif

Isso significa que, se você atualizar o número da versão usada, o servidor tratará isso como um novo recurso e não usará uma versão em cache. Se você basear seu número de versão no Subversion / CVS / etc. revisão significa que as alterações nas imagens referenciadas nos arquivos CSS serão notadas. Isso não é garantido com o primeiro esquema, ou seja, o URL images/happy.gifrelativo a /styles/screen.css?v=1235é/styles/images/happy.gif que não contém nenhuma informação de versão.

Eu implementei uma solução de armazenamento em cache usando essa técnica com servlets Java e simplesmente lide com solicitações /v/*com um servlet que delega para o recurso subjacente (ou seja /styles/screen.css). No modo de desenvolvimento que defina o cache cabeçalhos que informam ao cliente para sempre verificar o frescor do recurso com o servidor (isso normalmente resulta em um 304 se você delegar Tomcat de DefaultServleteo .css, .jsetc. arquivo não foi alterado), enquanto no modo de implantação Defino cabeçalhos que dizem "cache para sempre".


Simplesmente adicionar uma pasta que você pode renomear quando necessário funcionará se você usar apenas URLs relativos. E então você certifique-se de redirecionar para a pasta adequada a partir da pasta base, ou seja, em PHP: <?php header( 'Location: folder1/login.phtml' ); ?>.
Gruber

1
Usando o segundo método, uma alteração em um CSS invalidará cópias em cache de todas as imagens referenciadas com URLs relativos, o que pode ou não ser desejável.
18713 TomGen6:

5

Você pode simplesmente adicionar algum número aleatório com o URL CSS / JS como

example.css?randomNo=Math.random()

5

Para o ASP.NET, suponho que a próxima solução com opções avançadas (modo de depuração / versão, versões):

Arquivos Js ou Css incluídos desta maneira:

<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />

Global.JsPostfix e Global.CssPostfix são calculados da seguinte maneira em Global.asax:

protected void Application_Start(object sender, EventArgs e)
{
    ...
    string jsVersion = ConfigurationManager.AppSettings["JsVersion"];
    bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]);
    int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision;
    JsPostfix = "";
#if !DEBUG
    JsPostfix += ".min";
#endif      
    JsPostfix += ".js?" + jsVersion + "_" + buildNumber;
    if (updateEveryAppStart)
    {
        Random rand = new Random();
        JsPosfix += "_" + rand.Next();
    }
    ...
}

4

Eu recentemente resolvi isso usando Python. Aqui o código (deve ser fácil de adotar para outros idiomas):

def import_tag(pattern, name, **kw):
    if name[0] == "/":
        name = name[1:]
    # Additional HTML attributes
    attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
    try:
        # Get the files modification time
        mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
        include = "%s?%d" % (name, mtime)
        # this is the same as sprintf(pattern, attrs, include) in other
        # languages
        return pattern % (attrs, include)
    except:
        # In case of error return the include without the added query
        # parameter.
        return pattern % (attrs, name)

def script(name, **kw):
    return import_tag("""<script type="text/javascript" """ +\
        """ %s src="/%s"></script>""", name, **kw)

def stylesheet(name, **kw):
    return import_tag('<link rel="stylesheet" type="text/css" ' +\
        """%s href="/%s">', name, **kw) 

Esse código basicamente anexa o carimbo de data / hora dos arquivos como um parâmetro de consulta ao URL. A chamada da seguinte função

script("/main.css")

vai resultar em

<link rel="stylesheet" type="text/css"  href="/main.css?1221842734">

A vantagem, é claro, é que você nunca precisará alterar seu html novamente. Tocar no arquivo CSS acionará automaticamente uma invalidação de cache. Funciona muito bem e a sobrecarga não é perceptível.


os.stat () poderia criar um gargalo?
Hju

O @Richard stat pode ser um gargalo se o disco estiver muito lento e as solicitações forem muitas. Nesse caso, você pode armazenar em cache o registro de data e hora em algum lugar da memória e limpar esse cache a cada nova implantação. No entanto, essa complexidade não será necessária na maioria dos casos de uso.
pi.

4

Se você estiver usando git + PHP, poderá recarregar o script do cache sempre que houver uma alteração no repositório git, usando o seguinte código:

exec('git rev-parse --verify HEAD 2> /dev/null', $gitLog);
echo '  <script src="/path/to/script.js"?v='.$gitLog[0].'></script>'.PHP_EOL;

4

Se você é um desenvolvedor que deseja evitar o armazenamento em cache, a guia rede do Chrome desativou a opção de cache. Caso contrário, você poderá fazê-lo sem uma estrutura de renderização do servidor usando duas tags de script.

<script type="text/javascript">
    document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
    // can't use myfile.js stuff yet
</script>')
<script type="text/javascript">
    // do something with myfile.js
</script>

4

Esta pergunta é super antiga e aparece logo que alguém procura no Google esse problema. Esta não é uma resposta para a pergunta da maneira que o op quer, mas uma resposta para os desenvolvedores com esse problema durante o desenvolvimento e o teste. E não consigo postar uma nova pergunta sobre este tópico, pois ela será marcada como duplicada.

Como muitos outros, eu só queria remover o cache brevemente.

"keep caching consistent with the file" .. é muito aborrecido ..

De um modo geral, não me importo de carregar mais - mesmo carregar novamente arquivos que não foram alterados - na maioria dos projetos - é praticamente irrelevante. Durante o desenvolvimento de um aplicativo - estamos carregando principalmente do disco, por diante localhost:port -, esse increase in network trafficproblema não é uma questão de quebra de negócio .

A maioria dos projetos pequenos está apenas brincando - eles nunca acabam em produção. então para eles você não precisa de mais nada ..

Portanto, se você usa as Ferramentas de desenvolvimento do Chrome , pode seguir esta abordagem de desativar cache, como na imagem abaixo: como forçar o chrome a recarregar arquivos em cache

E se você tiver problemas de cache do firefox : como forçar a recarga de ativos no firefox

como desativar o cache no Firefox durante o desenvolvimento Faça isso apenas no desenvolvimento, você também precisará de um mecanismo para forçar o recarregamento da produção, pois os usuários usarão módulos antigos invalidados pelo cache se você atualizar o aplicativo com freqüência e não fornecer um mecanismo de sincronização de cache dedicado, como os descritos nas respostas acima.

Sim, essas informações já estão nas respostas anteriores, mas eu ainda precisava fazer uma pesquisa no google para encontrá-las.

Espero que esta resposta seja muito clara e agora você não precisa.


OP perguntou algo e respondeu outra coisa. Não se trata de carga forçada no local, mas na produção, e você não pode pedir aos usuários finais que sigam acima para desativar o cache etc.
Jitendra Pancholi

3

Parece que todas as respostas aqui sugerem algum tipo de versão no esquema de nomenclatura, que tem suas desvantagens.

Os navegadores devem estar cientes do que armazenar em cache e do que não armazenar em cache, lendo a resposta dos servidores da web, em particular os cabeçalhos http - por quanto tempo esse recurso é válido? este recurso foi atualizado desde a última vez que o recuperei? etcetera.

Se as coisas estiverem configuradas 'corretamente', apenas a atualização dos arquivos do seu aplicativo deve (em algum momento) atualizar os caches dos navegadores. Você pode, por exemplo, configurar seu servidor da Web para dizer ao navegador para nunca armazenar em cache arquivos (o que é uma má ideia).

Uma explicação mais detalhada de como isso funciona está aqui https://www.mnot.net/cache_docs/#WORK


3

Simplesmente adicione este código, onde você deseja recarregar com força (forçar o navegador a recarregar arquivos CSS / JS em cache) Faça isso dentro do .load para que não seja atualizado como um loop

 $( window ).load(function() {
   location.reload(true);
});

Não funciona no Chrome. Ainda a carregar ativos de cache de disco
Jason Kim

3

Basta usar o código do lado do servidor para adicionar a data do arquivo ... dessa forma, ele será armazenado em cache e recarregado apenas quando o arquivo for alterado

No ASP.NET

<link rel="stylesheet" href="~/css/custom.css?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />

<script type="text/javascript" src="~/js/custom.js?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>    

Isso pode ser simplificado para:

<script src="<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type="text/javascript"></script>

Adicionando um método de extensão ao seu projeto para estender a Página:

public static class Extension_Methods
{
    public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
    {
        string sFilePath = oPg.Server.MapPath(sRelPath);
        string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
        string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");

        return oPg.ResolveClientUrl(sRelPath) + "?d=" + sDateHashed;
    }
}

2

Sugiro implementar o seguinte processo:

  • versão seus arquivos css / js sempre que você implantar, algo como: screen.1233.css (o número pode ser sua revisão SVN se você usar um sistema de controle de versão)

  • reduza-os para otimizar os tempos de carregamento

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.