¿Cómo puedo forzar a los clientes a actualizar archivos JavaScript?

596

Actualmente estamos trabajando en una versión beta privada, por lo que todavía estamos en el proceso de realizar cambios bastante rápidos, aunque obviamente a medida que el uso comienza a aumentar, estaremos ralentizando este proceso. Dicho esto, un problema con el que nos estamos encontrando es que después de lanzar una actualización con nuevos archivos JavaScript, los navegadores del cliente aún usan la versión en caché del archivo y no ven la actualización. Obviamente, en una llamada de soporte, simplemente podemos informarles que realicen una ctrlF5actualización para garantizar que obtengan los archivos actualizados del servidor, pero sería preferible manejar esto antes de ese momento.

Nuestro pensamiento actual es simplemente adjuntar un número de versión al nombre de los archivos JavaScript y luego, cuando se realicen cambios, incremente la versión en el script y actualice todas las referencias. Esto definitivamente hace el trabajo, pero actualizar las referencias en cada versión podría ser engorroso.

Como estoy seguro de que no somos los primeros en lidiar con esto, pensé que lo tiraría a la comunidad. ¿Cómo se asegura de que los clientes actualicen su caché cuando actualiza su código? Si está utilizando el método descrito anteriormente, ¿está utilizando un proceso que simplifica el cambio?

AdamB
fuente

Respuestas:

529

Hasta donde yo sé, una solución común es agregar un ?<version>enlace src del script.

Por ejemplo:

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

¿Asumo en este punto que no hay una mejor manera que encontrar-reemplazar para incrementar estos "números de versión" en todas las etiquetas de script?

¿Podría tener un sistema de control de versiones que lo haga por usted? La mayoría de los sistemas de control de versiones tienen una forma de inyectar automáticamente el número de revisión en el registro, por ejemplo.

Se vería algo así:

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

Por supuesto, siempre hay mejores soluciones como esta .

Huppie
fuente
55
¿Alguien sabe si IE7 ignora esto? Parece ignorar los datos adjuntos y usar el archivo en caché cuando pruebo en la vista de comparabilidad de IE8.
Shane Reustle
44
Siempre supe que las cadenas de consulta son pares clave-valor como en? Ver = 123. ¡Gracias! :)
Ankur-m
66
Creo que no se trata de un número de versión más alto o más bajo, sino de cambiar el valor de las variables adjuntas a algo que el navegador aún no podría haber almacenado en caché.
Max Girkens
2
Recientemente encontramos el mismo problema, y ​​lo mejor que se me ocurrió fue una función simple que adjuntaba "? Mod = 123456" donde 123456 era la marca de tiempo de Unix de la fecha de modificación en el archivo. Eso parece solucionar los problemas y al mismo tiempo permite el almacenamiento en caché cuando corresponde. Sin embargo, todavía he visto que los navegadores ignoran esta directiva y usan el antiguo JS de todos modos, pero no sé si hay una "solución completa" elegante.
VPel
32
Para la conciencia: esto se considera un hack. Este método engaña al navegador para que piense que se está especificando un nuevo archivo, ya que simplemente mira el nombre completo del archivo sin interpretarlo. foo.js?1no es el mismo nombre que foo.js?2, por lo que el navegador pensará que son dos archivos diferentes. Una desventaja es que ambos archivos existirán simultáneamente en la memoria caché de los usuarios, ocupando espacio innecesario.
Lee White
89

Agregar la hora actual a la URL es de hecho una solución común. Sin embargo, también puede administrar esto en el nivel del servidor web, si lo desea. El servidor se puede configurar para enviar diferentes encabezados HTTP para archivos javascript.

Por ejemplo, para forzar el almacenamiento en caché del archivo por no más de 1 día, debe enviar:

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

Para la versión beta, si desea obligar al usuario a obtener siempre la última versión, usaría:

Cache-Control: no-cache, must-revalidate
Chase Seibert
fuente
3
¿Puedes ser más específico por favor?
Kreker
77
Está hablando de los encabezados enviados por el servidor web para cada archivo. Debe ser configurable en Apache, por ejemplo. Creo que este sería el mejor enfoque
Pierre de LESPINAY
1
¿Dónde configuras esto?
Diego
2
Para una aplicación web de desarrollo, tal vez sea una buena solución. Para un sitio de producción, donde no desea invalidar la memoria caché para siempre, no es una buena solución a menos que sepa que todos y cada uno de los navegadores del cliente objetivo han llegado al sitio. Me hace pensar en una característica potencial del servidor web: adaptar el parámetro max-age de acuerdo con una fecha de implementación configurada. Que sería increíble.
Claude Brisson
Chrome REQUIERE esta configuración para almacenar en caché correctamente. Sin ellos, Chrome almacenará en caché un archivo para siempre. Mozilla usa un valor predeterminado mucho más razonable. Ver más en: agiletribe.wordpress.com/2018/01/29/caching-for-chrome
AgilePro
42

Velocidad de página de Google: no incluya una cadena de consulta en la URL para recursos estáticos. La mayoría de los servidores proxy, especialmente Squid hasta la versión 3.0, no almacenan en caché los recursos con un "?" en su URL incluso si un encabezado público de control de caché está presente en la respuesta. Para habilitar el almacenamiento en caché del proxy para estos recursos, elimine las cadenas de consulta de las referencias a los recursos estáticos y, en su lugar, codifique los parámetros en los propios nombres de archivo.

En este caso, puede incluir la versión en la URL, por ejemplo: http://abc.com/ v1.2 /script.js y usar apache mod_rewrite para redirigir el enlace a http://abc.com/script.js . Cuando cambie la versión, el navegador del cliente actualizará el nuevo archivo.

Hắc Huyền Minh
fuente
Probé el? solución y en IE8 y obtengo un error de JavaScript. Mod rewrite es una opción, pero en la mayoría de los casos no tendremos tanto control sobre el servidor. Preferiría agregar la versión en el archivo js o tener una carpeta para cada versión
Karthik Sankar
@ Hắc Huyền Minh: Pero cuando el script se vuelva a cargar, no se debe volver a cargar desde el caché proxy ...
Stefan Steiger
34

Este uso ha quedado en desuso: https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache

Esta respuesta tiene solo 6 años de retraso, pero no veo esta respuesta en muchos lugares ... HTML5 ha introducido Application Cache que se utiliza para resolver este problema. Estaba descubriendo que el nuevo código de servidor que estaba escribiendo estaba bloqueando el antiguo JavaScript almacenado en los navegadores de las personas, por lo que quería encontrar una manera de expirar su JavaScript. Use un archivo de manifiesto que se vea así:

CACHE MANIFEST
# Aug 14, 2014
/mycode.js

NETWORK:
*

y genere este archivo con una nueva marca de tiempo cada vez que desee que los usuarios actualicen su caché. Como nota al margen, si agrega esto, el navegador no se volverá a cargar (incluso cuando un usuario actualice la página) hasta que el manifiesto se lo indique.

amos
fuente
Esta solución es realmente buena, siempre y cuando recuerdes actualizar el archivo de manifiesto :)
Mattis
24
Lea la documentación ya que esta característica se ha eliminado de los estándares web developer.mozilla.org/en-US/docs/Web/HTML/…
Flavia Obreja
1
FWIW, terminé no usando esta solución. Fue mucho más fácil usar / mantener el ?<version> enfoque.
amos
28

¿Qué tal agregar el tamaño del archivo como parámetro de carga?

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

Entonces, cada vez que actualiza el archivo, el parámetro "filever" cambia.

¿Qué tal cuando actualiza el archivo y sus resultados de actualización en el mismo tamaño de archivo? ¿Cuáles son las probabilidades?

Erik Corona
fuente
44
Esto usa etiquetas PHP y si uno usa PHP, de hecho es una buena idea.
Jerther
1
Creo que agregar la fecha de cambio sería mejor que el tamaño del archivo :)
Mazz
2
Mi pensamiento inicial es agregar un hash del archivo en lugar de la versión.
Mike Cheel
Supongo que también funciona si agrega una marca de tiempo Unix, ¿verdad? por ejemplo, '... file.js? filever = <? = time ()?>
garanda el
3
use filemtime ($ file) genera la marca de tiempo del archivo, con time () no puede usar caché ya que cambia cada segundo.
neoteknic
19

No todos los navegadores almacenan archivos en caché con '?' en eso. Lo que hice para asegurarme de que estaba en caché tanto como fue posible, incluí la versión en el nombre del archivo.

Así que en lugar de stuff.js?123hacerlostuff_123.js

Solía mod_redirect(creo) en apache have stuff_*.jspara irstuff.js

Echo dice reinstalar a Mónica
fuente
¿Podrías explicar qué hiciste en .htaccess con mod_redirect?
Venkat D.
3
Puede encontrar una explicación detallada de este método en particletree.com/notebook/…
Karl Bartel
3
Sería genial si pudiera incluir su .htaccesscódigo en su respuesta para referencia futura.
Fizzix
1
¿Qué navegadores no almacenan en caché los archivos con "?" ¿en eso?
rosell.dk
13

Para las páginas ASP.NET estoy usando lo siguiente

ANTES DE

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

DESPUÉS (fuerza de recarga)

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

Agregar DateTime.Now.Ticks funciona muy bien.

Ravi Ram
fuente
31
Este va en contra de todos los mecanismos de almacenamiento en caché en el lado del cliente. el parámetro ficticio debe reemplazarse por algo como "{versión principal} _ {versión_ menor} _ {número_construcción} _ {Revisión} que sería único para cada versión.
Tohid
14
Si bien esta es probablemente una buena solución en un entorno de desarrollo, no es apta para la producción. Esto deshabilitará por completo el caché cada vez que se cargue la página para el archivo. Imagine una carga de página de 10k por día con un archivo de 50Kb, representa 500Mb de archivo Javascript diariamente.
PhilDulac
@PhilDulac, puede cambiarlo de Ticks para devolver el valor de cadena del día, por ejemplo, o el mes o la semana del mes. En última instancia, solo le muestra cómo usar el enfoque? V
alex
3
@alex De hecho. Solo quería advertir que si el uso demostrado en la respuesta llega a la producción, puede tener impactos que no se muestran en el desarrollo.
PhilDulac
2
Una posible forma de garantizar que las nuevas copias se carguen una vez al día podría ser usar el tipo '<script src = "/ Scripts / pages / common.js? Ver <% = DateTime.Now.ToString (" aaaaMMdd ")%>" = "text / javascript"> </script> '. Entonces se carga una vez al comienzo del día, luego se almacena en caché.
Robb Sadler
7

Para ASP.NET, supongo que la próxima solución con opciones avanzadas (modo de depuración / liberación, versiones):

Archivos Js o Css incluidos de esta manera:

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

Global.JsPostfix y Global.CssPostfix se calcula de la siguiente manera en 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
fuente
Estoy usando .Ticks (vea mi respuesta en esta página)
Ravi Ram
5

La práctica común hoy en día es generar un código hash de contenido como parte del nombre del archivo para obligar al navegador, especialmente IE, a recargar los archivos javascript o css.

Por ejemplo,

vendedor. a7561fb0e9a071baadb9 .js
main. b746e3eb72875af2caa9 .js

Generalmente es el trabajo de las herramientas de compilación como webpack. Aquí hay más detalles si alguien quiere probar si está usando webpack.

código de schrodinger
fuente
4

Si está generando la página que enlaza con los archivos JS, una solución simple es agregar la marca de tiempo de la última modificación del archivo a los enlaces generados.

Esto es muy similar a la respuesta de Huppie, pero funciona en sistemas de control de versiones sin sustitución de palabras clave. También es mejor que agregar la hora actual, ya que eso evitaría el almacenamiento en caché incluso cuando el archivo no cambiara en absoluto.


fuente
Me gusta esta solución, ya que es más fácil de mantener. Si actualiza un archivo .js, eso es todo lo que tendrá que hacer. No es necesario actualizar también ninguna referencia al archivo, ya que su código agregará la última marca de tiempo actualizada automáticamente.
NL3294
3

La función jQuery getScript también se puede utilizar para garantizar que se cargue un archivo js cada vez que se carga la página.

Así es como lo hice:

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

Verifique la función en http://api.jquery.com/jQuery.getScript/

Por defecto, $ .getScript () establece la configuración de caché en falso. Esto agrega un parámetro de consulta con marca de tiempo a la URL de solicitud para garantizar que el navegador descargue el script cada vez que se solicite.

Michael Franz
fuente
8
Necesitamos almacenar en caché los archivos si no ocurren cambios.
Leo Lee
3

En PHP :

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

En HTML :

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

Cómo funciona:

En HTML, escriba el filepathnombre y el nombre como lo haría, pero solo en la función. PHP obtiene el filetimearchivo y devuelve el filepath+name+"?"+timeúltimo cambio

usuario1944129
fuente
3

Hemos estado creando un SaaS para los usuarios y proporcionándoles un script para adjuntar en la página de su sitio web, y no fue posible adjuntar una versión con el script ya que el usuario adjuntará el script a su sitio web para funcionalidades y no puedo forzarlos para cambiar la versión cada vez que actualizamos el script

Entonces, encontramos una manera de cargar la versión más nueva del script cada vez que el usuario llama al script original

el enlace del script proporcionado al usuario

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

el archivo 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
}

Explicación:

El usuario ha adjuntado el script que se le proporcionó en su sitio web y verificamos si el token único adjunto con el script existe o no utilizando el selector jQuery y, de lo contrario, lo cargamos dinámicamente con un token (o versión) más nuevo

Esto se llama el mismo script dos veces, lo que podría ser un problema de rendimiento, pero realmente resuelve el problema de obligar al script a no cargarse de la memoria caché sin poner la versión en el enlace del script real dado al usuario o cliente

Descargo de responsabilidad: no lo use si el rendimiento es un gran problema en su caso.

Aman Singh
fuente
2

En asp.net mvc puede usar @ DateTime.UtcNow.ToString () para el número de versión del archivo js. El número de versión cambia automáticamente con la fecha y obliga al navegador del cliente a actualizar automáticamente el archivo js. Estoy usando este método y esto funciona bien.

<script src="~/JsFilePath/[email protected]()"></script>
dragonal
fuente
Al igual que con otras soluciones sugeridas, esto hará que el archivo nunca se almacene en caché, lo que generalmente no es deseable. Siempre que no se hayan realizado cambios en el archivo, es probable que desee que el cliente use la versión en caché en lugar de descargar el archivo sin cambios nuevamente cada vez.
Philip Stratford
Puede usar el siguiente código para su razón, archivo de caché con número de versión <script src = "~/JsFilePath/JsFile.js?v=@GetAppVersionNumber ()"> </script>
dragonal
2

location.reload (verdadero);

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

Llamo dinámicamente a esta línea de código para asegurarme de que javascript se haya recuperado del servidor web en lugar de la memoria caché del navegador para evitar este problema.

Megan
fuente
Agregar el onload="location.reload();"formulario a mi formulario me permite obtener el nuevo JS después de una actualización en lugar de reiniciar mi página. Esta es una solución mucho más elegante. ¡Gracias!
ZX9
Gracias, podría usar esto con una comprobación si se reconoce la ip pero no se ha utilizado para iniciar sesión desde la última actualización. Realice esto en la página de índice después del inicio de sesión inicial de los usuarios.
Fi Horan
onload = "location.reload (true);" Lo anterior no me funcionó (utilizando el matraz y la versión actual de Chrome) también: w3schools.com/jsref/met_loc_reload.asp
aspiringGuru
1

Una solución es agregar una cadena de consulta con una marca de tiempo a la URL al recuperar el recurso. Esto aprovecha el hecho de que un navegador no almacenará en caché los recursos obtenidos de las URL con cadenas de consulta en ellos.

Sin embargo, probablemente no desee que el navegador no almacene en caché estos recursos; es más probable que desee almacenarlos en caché, pero desea que el navegador obtenga una nueva versión del archivo cuando esté disponible.

La solución más común parece ser incrustar una marca de tiempo o un número de revisión en el nombre del archivo. Esto es un poco más de trabajo, porque su código debe modificarse para solicitar los archivos correctos, pero significa que, por ejemplo, la versión 7 de su snazzy_javascript_file.js(es decir, snazzy_javascript_file_7.js) se almacena en caché en el navegador hasta que lance la versión 8, y luego su código cambia a buscar en su snazzy_javascript_file_8.jslugar.

Richard Turner
fuente
1

La ventaja de usar un file.js?V=1sobre unfileV1.js es que no necesita almacenar varias versiones de los archivos JavaScript en el servidor.

El problema que veo con file.js?V=1 es que puede tener un código dependiente en otro archivo JavaScript que se rompe al usar la nueva versión de las utilidades de la biblioteca.

En aras de la compatibilidad con versiones anteriores, creo que es mucho mejor usarlo jQuery.1.3.jspara sus nuevas páginas y dejar que las páginas existentes lo usen jQuery.1.1.js, hasta que esté listo para actualizar las páginas más antiguas, si es necesario.


fuente
1

Use una GETvariable de versión para evitar el almacenamiento en caché del navegador.

Agregar ?v=AUTO_INCREMENT_VERSIONal final de su URL evita el almacenamiento en caché del navegador, evitando todos los scripts almacenados en caché.

Derek Adair
fuente
1

Mi colega acaba de encontrar una referencia a ese método justo después de publicar (en referencia a css) en http://www.stefanhayden.com/blog/2006/04/03/css-caching-hack/ . Es bueno ver que otros lo están usando y parece funcionar. ¿Asumo en este punto que no hay una mejor manera que encontrar-reemplazar para incrementar estos "números de versión" en todas las etiquetas de script?

AdamB
fuente
1

Cache Busting en ASP.NET Core a través de un asistente de etiquetas lo manejará por usted y permitirá que su navegador mantenga scripts / css en caché hasta que el archivo cambie. Simplemente agregue la etiqueta ayudante asp-append-version = "true" a su script (js) o etiqueta de enlace (css):

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

Dave Paquette tiene un buen ejemplo y una explicación del almacenamiento en caché aquí (parte inferior de la página) Almacenamiento en caché

ccherwin
fuente
¿Esto no funciona en ASP.NET normal? Intenté agregar la versión asp-append-version a mi etiqueta de script y todo lo que ve el navegador es la etiqueta de script exactamente como aparece en la fuente, incluido el atributo asp-append-version.
tolsen64
Este es un atributo .NET Core asociado con Tag Helpers. Agrega el nombre del script con una versión para que el servidor / navegador siempre vea la última versión y descargas
ccherwin
0

¿La solución más simple? No permita que el navegador se almacene en caché. Agregue la hora actual (en ms) como una consulta.

(Todavía está en versión beta, por lo que podría presentar un caso razonable para no optimizar el rendimiento. Pero YMMV aquí).

pcorcoran
fuente
13
En mi humilde opinión, esta es una solución pobre. ¿Qué sucede si no está en BETA y saca una actualización importante?
d -_- b
0

Una forma simple 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
fuente
Esto provoca una redirección que es una solución subóptima pero funcional en cuanto al rendimiento.
twicejr
0

A continuación funcionó para mí:

<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
fuente