Como forçar os clientes a atualizar arquivos JavaScript?

595

No momento, estamos trabalhando em uma versão beta privada e ainda estamos no processo de fazer alterações razoavelmente rápidas, embora, obviamente, à medida que o uso esteja começando a acelerar, estaremos desacelerando esse processo. Dito isso, um problema que estamos enfrentando é que, após enviar uma atualização com novos arquivos JavaScript, os navegadores clientes ainda usam a versão em cache do arquivo e não veem a atualização. Obviamente, em uma chamada de suporte, podemos simplesmente informá-los a fazer uma ctrlF5atualização para garantir que eles obtenham os arquivos atualizados do servidor, mas seria preferível lidar com isso antes desse período.

Nosso pensamento atual é simplesmente anexar um número de versão ao nome dos arquivos JavaScript e, quando forem feitas alterações, incrementar a versão no script e atualizar todas as referências. Isso definitivamente faz o trabalho, mas a atualização das referências em cada versão pode ser complicada.

Como tenho certeza de que não somos os primeiros a lidar com isso, pensei em jogá-lo para a comunidade. Como você garante que os clientes atualizem seu cache ao atualizar seu código? Se você estiver usando o método descrito acima, está usando um processo que simplifica a alteração?

AdamB
fonte

Respostas:

529

Tanto quanto sei, uma solução comum é adicionar um ?<version>ao link src do script.

Por exemplo:

<script type="text/javascript" src="myfile.js?1500"></script>

Presumo que, neste momento, não exista uma maneira melhor do que encontrar e substituir para incrementar esses "números de versão" em todas as tags de script?

Você pode ter um sistema de controle de versão para isso? A maioria dos sistemas de controle de versão tem uma maneira de injetar automaticamente o número da revisão no check-in, por exemplo.

Seria algo como isto:

<script type="text/javascript" src="myfile.js?$$REVISION$$"></script>

Obviamente, sempre existem soluções melhores como esta .

Huppie
fonte
5
Alguém sabe se o IE7 ignora isso? Parece ignorar os dados anexados e usar o arquivo em cache quando testo no modo de comparabilidade do IE8.
precisa saber é o seguinte
4
Eu sempre soube que as cadeias de consulta são par de valores-chave como em? Ver = 123. Obrigado! :)
Ankur-m
6
Eu acho que não se trata de número de versão superior ou inferior, mas de alterar o valor das variáveis ​​anexadas para algo que o navegador ainda não poderia ter armazenado em cache.
precisa saber é o seguinte
2
Recentemente, encontramos o mesmo problema, e o melhor que pude apresentar foi uma função simples que anexava "? Mod = 123456", em que 123456 era o carimbo de data e hora unix da data modificada no arquivo. Isso parece corrigir os problemas enquanto ainda permite o armazenamento em cache quando apropriado. No entanto, ainda vi navegadores ignorarem essa diretiva e usarem JS antigo de qualquer maneira, mas não sei se existe uma "correção completa" elegante.
VPel
31
Para conscientização: isso é considerado um hack. Esse método leva o navegador a pensar que um novo arquivo está sendo especificado, pois simplesmente analisa o nome completo do arquivo sem interpretá-lo. foo.js?1não é o mesmo nome foo.js?2, então o navegador pensará que são dois arquivos diferentes. Uma desvantagem é que ambos os arquivos existirão simultaneamente no cache dos usuários, ocupando espaço desnecessário.
Lee White
88

Anexar o horário atual ao URL é realmente uma solução comum. No entanto, você também pode gerenciar isso no nível do servidor da web, se desejar. O servidor pode ser configurado para enviar diferentes cabeçalhos HTTP para arquivos javascript.

Por exemplo, para forçar o armazenamento em cache do arquivo por mais de um dia, você deve enviar:

Cache-Control: max-age=86400, must-revalidate

Para a versão beta, se você deseja forçar o usuário a sempre obter as últimas, use:

Cache-Control: no-cache, must-revalidate
Chase Seibert
fonte
3
Você pode por gentileza ser mais específico?
Kreker
6
Ele está falando sobre os cabeçalhos enviados pelo servidor da web para cada arquivo. Deve ser configurável no Apache, por exemplo. Eu acho que essa seria a melhor
abordagem.
1
onde você configura isso?
21716 Diego
2
Para um webapp de desenvolvimento, talvez seja uma boa solução. Para um site de produção, no qual você não deseja invalidar o cache para sempre, não é uma boa solução, a menos que você saiba que todo e qualquer navegador cliente de destino chegou ao site. Isso me faz pensar em um recurso potencial do servidor da Web: adaptar o parâmetro max-age de acordo com uma data de implantação configurada. Isso seria demais.
Claude Brisson
O Chrome EXIGE essas configurações para fazer o cache corretamente. Sem eles, o Chrome armazenará em cache um arquivo para sempre. O Mozilla usa um padrão muito mais razoável. Veja mais em: agiletribe.wordpress.com/2018/01/29/caching-for-chrome
AgilePro
42

Velocidade da página do Google: não inclua uma string de consulta no URL para recursos estáticos. A maioria dos proxies, principalmente o Squid up pela versão 3.0, não armazena em cache os recursos com um "?" na URL deles, mesmo que um cabeçalho de controle de cache: público esteja presente na resposta. Para habilitar o cache de proxy para esses recursos, remova as cadeias de consulta das referências a recursos estáticos e, em vez disso, codifique os parâmetros nos nomes dos arquivos.

Nesse caso, você pode incluir a versão no URL ex: http://abc.com/ v1.2 /script.js e usar o apache mod_rewrite para redirecionar o link para http://abc.com/script.js . Quando você altera a versão, o navegador do cliente atualiza o novo arquivo.

Hắc Huyền Minh
fonte
Eu tentei o? solução e no IE8 e recebo um erro de javascript. A reescrita de mod é uma opção, mas na maioria dos casos não temos tanto controle sobre o servidor. Eu preferiria anexando a versão no arquivo js em si ou ter uma pasta para cada versão
Karthik Sankar
@ HAC Huyen Minh: Mas quando o script deve ser recarregado, ele não deve ser recarregado a partir do proxy-cache ...
Stefan Steiger
34

Este uso foi preterido: https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache

Esta resposta está com apenas 6 anos de atraso, mas não a vejo em muitos lugares ... O HTML5 introduziu o Cache de Aplicativo, que é usado para resolver esse problema. Eu estava descobrindo que o novo código do servidor que estava escrevendo estava travando o javascript antigo armazenado nos navegadores das pessoas, então eu queria encontrar uma maneira de expirar o javascript deles. Use um arquivo de manifesto parecido com este:

CACHE MANIFEST
# Aug 14, 2014
/mycode.js

NETWORK:
*

e gere esse arquivo com um novo registro de data e hora sempre que você desejar que os usuários atualizem seu cache. Como observação lateral, se você adicionar isso, o navegador não será recarregado (mesmo quando um usuário atualizar a página) até que o manifesto solicite.

amos
fonte
Esta solução é realmente bom, contanto que você lembre-se de atualizar o arquivo de manifesto :)
Mattis
24
Leia a documentação, pois esse recurso foi removido dos padrões da Web developer.mozilla.org/en-US/docs/Web/HTML/…
Flavia Obreja
1
FWIW, acabei não usando esta solução. Era muito mais fácil usar / manter a ?<version> abordagem.
Am
28

Que tal adicionar o tamanho do arquivo como um parâmetro de carregamento?

<script type='text/javascript' src='path/to/file/mylibrary.js?filever=<?=filesize('path/to/file/mylibrary.js')?>'></script>

Portanto, toda vez que você atualiza o arquivo, o parâmetro "filever" é alterado.

Que tal quando você atualiza o arquivo e sua atualização resulta no mesmo tamanho de arquivo? Quais são as hipóteses?

Erik Corona
fonte
4
Isso usa tags PHP e, se alguém usa PHP, é realmente uma boa ideia.
Jerther
1
Eu acho que a adição do CHANGEDATE seria melhor do que o tamanho do arquivo :)
Mazz
2
Meu pensamento inicial é adicionar um hash do arquivo em vez da versão.
Mike Cheel
Eu suponho que ele também funcione se adicionar um carimbo de data / hora Unix, certo? Por exemplo '... file.js? filever = <? = time ()?>
garanda
3
use filemtime ($ file) ele gera o registro de data e hora do arquivo, com time () você não pode usar cache, pois ele muda a cada segundo.
neoteknic
19

Nem todos os navegadores armazenam arquivos em cache com '?' iniciar. O que fiz para garantir que fosse armazenado em cache o máximo possível, incluí a versão no nome do arquivo.

Então, ao invés de stuff.js?123, eu fizstuff_123.js

Eu usei mod_redirect(eu acho) no apache have stuff_*.jspara irstuff.js

Echo diz Restabelecer Monica
fonte
Você poderia elaborar o que fez no .htaccess com o mod_redirect?
Venkat D.
3
Uma explicação detalhada desse método pode ser encontrada em particletree.com/notebook/…
Karl Bartel
3
Seria ótimo se você pudesse incluir seu .htaccesscódigo em sua resposta para referência futura.
Fizzix 29/02
1
Quais navegadores não armazenam em cache os arquivos com "?" iniciar?
Rosell.dk 17/06/19
13

Para páginas ASP.NET, estou usando o seguinte

ANTES

<script src="/Scripts/pages/common.js" type="text/javascript"></script>

DEPOIS (força recarregar)

<script src="/Scripts/pages/common.js?ver<%=DateTime.Now.Ticks.ToString()%>" type="text/javascript"></script>

Adicionar o DateTime.Now.Ticks funciona muito bem.

Ravi Ram
fonte
31
Este vai contra todo o mecanismo de armazenamento em cache no lado do cliente. O parâmetro dummy deve ser substituído por algo como "{versão principal} _ {versão menor de versão} _ {build_number} _ {Revisão}, que seria exclusiva para cada versão.
Tohid
14
Embora essa seja provavelmente uma solução divina em um ambiente de desenvolvimento, ela não é adequada para produção. Isso desativará completamente o cache sempre que a página for carregada para o arquivo. Imagine carregar 10k páginas por dia com um arquivo de 50Kb, representa 500Mb de arquivo Javascript diariamente.
PhilDulac
@PhilDulac, você pode alterá-lo de Ticks para retornar o valor da sequência do dia, por exemplo, ou o mês ou a semana do mês. Em última análise é apenas mostrando-lhe como usar o v abordagem?
alex
3
@alex De fato. Eu só queria avisar que, se o uso demonstrado na resposta chegar à produção, ele poderá ter impactos que não aparecem no desenvolvimento.
PhilDulac
2
Uma maneira possível de garantir que novas cópias sejam carregadas uma vez por dia pode ser usando o tipo '<script src = "/ Scripts / páginas / common.js? Ver <% = DateTime.Now.ToString (" aaaamMMdd ")%>" = "text / javascript"> </script> '. Portanto, é carregado uma vez no início do dia e depois armazenado em cache.
Robb Sadler
7

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();
    }
    ...
}
Ivan Kochurkin
fonte
Eu estou usando .Ticks (ver minha resposta desta página)
Ravi Ram
5

Atualmente, a prática comum é gerar um código de hash de conteúdo como parte do nome do arquivo para forçar o navegador, especialmente o IE, a recarregar os arquivos javascript ou css.

Por exemplo,

fornecedor. a7561fb0e9a071baadb9 .js
main. b746e3eb72875af2caa9 .js

Geralmente, é o trabalho das ferramentas de construção, como o webpack. Aqui estão mais detalhes se alguém quiser experimentar se você estiver usando o webpack.

código de schrodinger
fonte
4

Se você estiver gerando a página que vincula aos arquivos JS, uma solução simples anexará o carimbo de data e hora da última modificação do arquivo aos links gerados.

Isso é muito semelhante à resposta de Huppie, mas funciona em sistemas de controle de versão sem substituição de palavra-chave. Também é melhor do que acrescentar a hora atual, pois isso impediria o armazenamento em cache mesmo quando o arquivo não foi alterado.


fonte
Gosto dessa solução, pois é mais fácil de manter. Se você atualizar um arquivo .js, é tudo o que precisa fazer. Não é necessário atualizar também nenhuma referência ao arquivo, pois seu código adicionará o último registro de data e hora atualizado automaticamente.
NL3294
3

A função jQuery getScript também pode ser usada para garantir que um arquivo js seja realmente carregado toda vez que a página for carregada.

Foi assim que eu fiz:

$(document).ready(function(){
    $.getScript("../data/playlist.js", function(data, textStatus, jqxhr){
         startProgram();
    });
});

Verifique a função em http://api.jquery.com/jQuery.getScript/

Por padrão, $ .getScript () define a configuração do cache como false. Isso anexa um parâmetro de consulta com registro de data e hora ao URL da solicitação para garantir que o navegador faça o download do script toda vez que for solicitado.

Michael Franz
fonte
8
Precisamos armazenar arquivos em cache se nenhuma alteração ocorrer.
Leo Lee
3

Em PHP :

function latest_version($file_name){
    echo $file_name."?".filemtime($_SERVER['DOCUMENT_ROOT'] .$file_name);
}

Em HTML :

<script type="text/javascript" src="<?php latest_version('/a-o/javascript/almanacka.js'); ?>">< /script>

Como funciona:

Em HTML, escreva o filepathnome e como você faria, mas apenas na função. O PHP obtém o filetimearquivo e retorna as filepath+name+"?"+timealterações mais recentes

user1944129
fonte
3

Criamos um SaaS para usuários e fornecemos a eles um script para anexar na página do site deles, e não foi possível anexar uma versão com o script, pois o usuário anexará o script ao site para funcionalidades e não posso forçá-los para alterar a versão cada vez que atualizamos o script

Então, encontramos uma maneira de carregar a versão mais recente do script sempre que o usuário chama o script original

o link do script fornecido ao usuário

<script src="https://thesaasdomain.com/somejsfile.js" data-ut="user_token"></script>

o arquivo de script

if($('script[src^="https://thesaasdomain.com/somejsfile.js?"]').length !== 0) {
   init();
} else {
   loadScript("https://thesaasdomain.com/somejsfile.js?" + guid());
}

var loadscript = function(scriptURL) {
   var head = document.getElementsByTagName('head')[0];
   var script = document.createElement('script');
   script.type = 'text/javascript';
   script.src = scriptURL;
   head.appendChild(script);
}

var guid = function() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

var init = function() {
    // our main code
}

Explicação:

O usuário anexou o script fornecido a eles em seu site e verificamos se o token exclusivo anexado ao script existe ou não usando o seletor jQuery e, se não estiver, carregue-o dinamicamente com o token (ou versão mais recente)

Isso é chamado o mesmo script duas vezes, o que pode ser um problema de desempenho, mas realmente resolve o problema de forçar o script a não carregar do cache sem colocar a versão no link de script real fornecido ao usuário ou cliente

Isenção de responsabilidade: não use se o desempenho for um grande problema no seu caso.

Aman Singh
fonte
2

No asp.net mvc, você pode usar @ DateTime.UtcNow.ToString () para o número da versão do arquivo js. O número da versão é alterado automaticamente com a data e você força o navegador dos clientes a atualizar automaticamente o arquivo js. Eu estou usando esse método e isso funciona bem.

<script src="~/JsFilePath/[email protected]()"></script>
dragonal
fonte
Como com outras soluções sugeridas, isso fará com que o arquivo nunca seja armazenado em cache, o que geralmente é indesejável. Desde que nenhuma alteração tenha sido feita no arquivo, você provavelmente deseja que o cliente use a versão em cache em vez de baixar o arquivo inalterado novamente todas as vezes.
Philip Stratford
Você pode usar o código abaixo por seu motivo, arquivo de cache com o número da versão <script src = "~/JsFilePath/JsFile.js?v=@GetAppVersionNumber ()"> </script>
dragonal
2

location.reload (true);

Vejo https://www.w3schools.com/jsref/met_loc_reload.asp

Eu chamo dinamicamente essa linha de código para garantir que o javascript tenha sido recuperado do servidor da Web em vez do cache do navegador para evitar esse problema.

Megan
fonte
Adicionar o onload="location.reload();"formulário ao meu formulário permite obter o novo JS após uma atualização, em vez de reiniciar minha página. Esta é uma solução muito mais elegante. Obrigado!
ZX9 6/11/19
Obrigado, poderia usar isso com uma verificação se o ip for reconhecido, mas não tiver sido usado para fazer login desde a última atualização, executar isso na página de índice após o login inicial dos usuários.
Fi Horan
onload = "location.reload (true);" O exemplo acima não funcionou para mim (usando o balão e a versão atual do Chrome) também: w3schools.com/jsref/met_loc_reload.asp
aspiringGuru
1

Uma solução é anexar uma string de consulta com um carimbo de data / hora no URL ao buscar o recurso. Isso tira vantagem do fato de um navegador não armazenar em cache os recursos buscados nos URLs com as cadeias de consulta.

Você provavelmente não deseja que o navegador não armazene em cache esses recursos; é mais provável que você os queira em cache, mas deseja que o navegador busque uma nova versão do arquivo quando ele estiver disponível.

A solução mais comum parece ser incorporar um carimbo de data / hora ou número de revisão no próprio nome do arquivo. Isso é um pouco mais trabalhoso, porque seu código precisa ser modificado para solicitar os arquivos corretos, mas significa que, por exemplo, a versão 7 do seu snazzy_javascript_file.js(ie snazzy_javascript_file_7.js) é armazenada em cache no navegador até o lançamento da versão 8 e, em seguida, seu código muda para buscar em seu snazzy_javascript_file_8.jslugar.

Richard Turner
fonte
1

A vantagem de usar um file.js?V=1over a fileV1.jsé que você não precisa armazenar várias versões dos arquivos JavaScript no servidor.

O problema que vejo file.js?V=1é que você pode ter um código dependente em outro arquivo JavaScript que é interrompido ao usar a nova versão dos utilitários da biblioteca.

Por uma questão de compatibilidade com versões anteriores, acho muito melhor usar jQuery.1.3.jssuas novas páginas e permitir que as páginas existentes sejam usadas jQuery.1.1.jsaté que você esteja pronto para atualizar as páginas mais antigas, se necessário.


fonte
1

Use uma GETvariável de versão para impedir o cache do navegador.

Anexar ?v=AUTO_INCREMENT_VERSIONao final do seu URL impede o cache do navegador, evitando todo e qualquer script em cache.

Derek Adair
fonte
1

Meu colega acabou de encontrar uma referência a esse método logo depois que eu postei (em referência ao css) em http://www.stefanhayden.com/blog/2006/04/03/css-caching-hack/ . É bom ver que outras pessoas estão usando e parece funcionar. Presumo que, neste momento, não exista uma maneira melhor do que localizar e substituir para incrementar esses "números de versão" em todas as tags de script?

AdamB
fonte
1

Embora seja específico da estrutura, o Django 1.4 tem essa função, que funciona de maneira semelhante ao link para o site 'greenfelt' na resposta acima.

Trent
fonte
1

A invasão de cache no ASP.NET Core por meio de um auxiliar de marca tratará disso para você e permitirá que seu navegador mantenha os scripts em cache / css até que o arquivo seja alterado. Basta adicionar a tag helper asp-append-version = "true" à sua tag de script (js) ou link (css):

<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true"/>

Dave Paquette tem um bom exemplo e explicação do bloqueio de cache aqui (na parte inferior da página) Cache rebentando

ccherwin
fonte
Isso não funciona no ASP.NET normal? Tentei adicionar a versão asp-append-version à minha tag de script e tudo que o navegador vê é a tag de script exatamente como aparece na fonte, incluindo o atributo asp-append-version.
tolsen64
Este é um atributo do .NET Core associado aos Auxiliares de tags. Ele acrescenta o nome do script com uma versão para que o servidor / navegador sempre vê a versão mais recente e os downloads
ccherwin
0

Solução mais simples? Não deixe o navegador em cache. Anexe a hora atual (em ms) como uma consulta.

(Você ainda está na versão beta, portanto, você poderia argumentar razoavelmente por não otimizar o desempenho. Mas YMMV aqui.)

pcorcoran
fonte
13
IMHO esta é uma solução ruim. E se você não estiver no BETA e enviar uma atualização importante?
precisa
0

Uma maneira simples. Editar htaccess

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} \.(jpe?g|bmp|png|gif|css|js|mp3|ogg)$ [NC]
RewriteCond %{QUERY_STRING} !^(.+?&v33|)v=33[^&]*(?:&(.*)|)$ [NC]
RewriteRule ^ %{REQUEST_URI}?v=33 [R=301,L]
Goran Siriev
fonte
Isso faz com que um redirecionamento seja, em termos de desempenho, uma solução subótima, mas funcional.
precisa saber é o seguinte
0

Abaixo trabalhou para mim:

<head>
<meta charset="UTF-8">
<meta http-equiv="cache-control" content="no-cache, must-revalidate, post-check=0, pre-check=0" />
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" />
</head>
H.Ostwal
fonte