Comment puis-je forcer les clients à actualiser les fichiers JavaScript?

596

Nous travaillons actuellement dans une version bêta privée et nous sommes donc toujours en train de faire des changements assez rapides, bien que de toute évidence, comme l'utilisation commence à augmenter, nous allons ralentir ce processus. Cela étant dit, un problème que nous rencontrons est qu'après avoir poussé une mise à jour avec de nouveaux fichiers JavaScript, les navigateurs clients utilisent toujours la version mise en cache du fichier et ils ne voient pas la mise à jour. Évidemment, lors d'un appel de support, nous pouvons simplement les informer de faire un ctrlF5rafraîchissement pour s'assurer qu'ils obtiennent les fichiers à jour du serveur, mais il serait préférable de gérer cela avant cette date.

Notre idée actuelle est de simplement attacher un numéro de version au nom des fichiers JavaScript, puis lorsque des modifications sont apportées, d'incrémenter la version sur le script et de mettre à jour toutes les références. Cela fait définitivement le travail, mais la mise à jour des références sur chaque version peut devenir fastidieuse.

Comme je suis sûr que nous ne sommes pas les premiers à faire face à cela, j'ai pensé que j'allais le jeter à la communauté. Comment assurez-vous que les clients mettent à jour leur cache lorsque vous mettez à jour votre code? Si vous utilisez la méthode décrite ci-dessus, utilisez-vous un processus qui simplifie le changement?

AdamB
la source

Réponses:

529

Pour autant que je sache, une solution courante consiste à ajouter un ?<version>au lien src du script.

Par exemple:

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

Je suppose à ce stade qu'il n'y a pas de meilleur moyen que find-replace pour incrémenter ces "numéros de version" dans toutes les balises de script?

Vous pourriez avoir un système de contrôle de version qui le fasse pour vous? La plupart des systèmes de contrôle de version ont un moyen d'injecter automatiquement le numéro de révision lors de l'enregistrement par exemple.

Cela ressemblerait à quelque chose comme ceci:

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

Bien sûr, il existe toujours de meilleures solutions comme celle-ci .

Huppie
la source
5
Est-ce que quelqu'un sait si IE7 ignore cela? Il semble ignorer les données ajoutées et utiliser le fichier mis en cache lorsque je teste dans la vue de comparabilité IE8.
Shane Reustle
4
J'ai toujours su que les chaînes de requête sont une paire clé-valeur comme dans? Ver = 123. Merci! :)
Ankur-m
6
Je pense qu'il ne s'agit pas d'un numéro de version supérieur ou inférieur mais de changer la valeur des variables ajoutées en quelque chose que le navigateur n'aurait pas encore pu mettre en cache.
Max Girkens
2
Nous avons récemment rencontré le même problème, et le mieux que j'ai pu trouver était une fonction simple qui attachait "? Mod = 123456" où 123456 était l'horodatage unix de la date modifiée sur le fichier. Cela semble résoudre les problèmes tout en permettant la mise en cache, le cas échéant. Cependant, j'ai encore vu des navigateurs ignorer cette directive et utiliser l'ancien JS de toute façon, mais je ne sais pas s'il existe une élégante «solution complète».
VPel
32
Pour la sensibilisation: cela est considéré comme un hack. Cette méthode incite le navigateur à penser qu'un nouveau fichier est spécifié, car il regarde simplement le nom de fichier complet sans l'interpréter. foo.js?1n'est pas le même nom que foo.js?2, donc le navigateur pensera qu'il s'agit de deux fichiers différents. Un inconvénient est que les deux fichiers existeront simultanément dans le cache des utilisateurs, occupant ainsi un espace inutile.
Lee White
89

Ajouter l'heure actuelle à l'URL est en effet une solution courante. Cependant, vous pouvez également gérer cela au niveau du serveur Web, si vous le souhaitez. Le serveur peut être configuré pour envoyer différents en-têtes HTTP pour les fichiers javascript.

Par exemple, pour forcer la mise en cache du fichier pendant plus d'un jour, vous enverrez:

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

Pour la version bêta, si vous voulez forcer l'utilisateur à toujours obtenir la dernière version, vous utiliseriez:

Cache-Control: no-cache, must-revalidate
Chase Seibert
la source
3
pouvez-vous être plus précis?
Kreker
7
Il parle des en-têtes envoyés par le serveur Web pour chaque fichier. Doit être configurable dans Apache par exemple. Je pense que ce serait la meilleure approche
Pierre de LESPINAY
1
où configurez-vous cela?
Diego
2
Pour une webapp de développement, c'est peut-être une bonne solution. Pour un site de production, où vous ne voulez pas invalider le cache pour toujours, ce n'est pas une bonne solution, sauf si vous savez que chaque navigateur client cible est venu sur le site. Cela me fait penser à une fonctionnalité potentielle de serveur Web: adapter le paramètre max-age en fonction d'une date de déploiement configurée. Ce serait génial.
Claude Brisson
Chrome EXIGE ces paramètres afin de mettre en cache correctement. Sans eux, Chrome mettra en cache un fichier pour toujours. Mozilla utilise une valeur par défaut beaucoup plus raisonnable. Voir plus à: agiletribe.wordpress.com/2018/01/29/caching-for-chrome
AgilePro
42

Google Page-Speed: n'incluez pas de chaîne de requête dans l'URL des ressources statiques. La plupart des serveurs proxy, notamment Squid jusqu'à la version 3.0, ne mettent pas en cache les ressources avec un "?" dans leur URL même si un en-tête Cache-control: public est présent dans la réponse. Pour activer la mise en cache du proxy pour ces ressources, supprimez les chaînes de requête des références aux ressources statiques et codez plutôt les paramètres dans les noms de fichiers eux-mêmes.

Dans ce cas, vous pouvez inclure la version dans l'URL ex: http://abc.com/ v1.2 /script.js et utiliser apache mod_rewrite pour rediriger le lien vers http://abc.com/script.js . Lorsque vous modifiez la version, le navigateur client mettra à jour le nouveau fichier.

Hắc Huyền Minh
la source
J'ai essayé le? solution et dans IE8 et j'obtiens une erreur javascript. La réécriture des modules est une option mais dans la plupart des cas, nous n'aurons pas autant de contrôle sur le serveur. Je préférerais ajouter la version dans le fichier js lui-même ou avoir un dossier pour chaque version
Karthik Sankar
@ Hắc Huyền Minh: Mais quand le script doit être rechargé, il ne doit pas être rechargé depuis le proxy-cache ...
Stefan Steiger
34

Cette utilisation a été supprimée: https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache

Cette réponse n'a que 6 ans de retard, mais je ne vois pas cette réponse à de nombreux endroits ... HTML5 a introduit le cache d'application qui est utilisé pour résoudre ce problème. Je découvrais que le nouveau code de serveur que j'écrivais faisait planter le vieux javascript stocké dans les navigateurs des gens, alors je voulais trouver un moyen de faire expirer leur javascript. Utilisez un fichier manifeste qui ressemble à ceci:

CACHE MANIFEST
# Aug 14, 2014
/mycode.js

NETWORK:
*

et générer ce fichier avec un nouvel horodatage chaque fois que vous souhaitez que les utilisateurs mettent à jour leur cache. En remarque, si vous ajoutez cela, le navigateur ne se rechargera pas (même lorsqu'un utilisateur actualise la page) jusqu'à ce que le manifeste le lui dise.

amos
la source
Cette solution est vraiment bonne, tant que vous vous souvenez de mettre à jour le fichier manifeste :)
Mattis
24
Veuillez lire la documentation car cette fonctionnalité a été supprimée des standards Web developer.mozilla.org/en-US/docs/Web/HTML/…
Flavia Obreja
1
FWIW, j'ai fini par ne pas utiliser cette solution. Il était beaucoup plus facile d'utiliser / de maintenir l' ?<version> approche.
amos
28

Que diriez-vous d'ajouter la taille du fichier comme paramètre de chargement?

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

Ainsi, chaque fois que vous mettez à jour le fichier, le paramètre "filever" change.

Qu'en est-il lorsque vous mettez à jour le fichier et que votre mise à jour donne la même taille de fichier? quelles sont les chances?

Erik Corona
la source
4
Cela utilise des balises PHP et si l'on utilise PHP, c'est en effet une bonne idée.
Jerther
1
Je pense que l'ajout de la modification serait mieux que la taille du fichier :)
Mazz
2
Ma pensée initiale est d'ajouter un hachage du fichier au lieu de la version.
Mike Cheel
Je suppose que cela fonctionne également si vous ajoutez un horodatage Unix, non? par exemple '... file.js? filever = <? = time ()?>
garanda
3
utilisez filemtime ($ file), il affiche l'horodatage du fichier, avec time () vous ne pouvez pas utiliser le cache car il change toutes les secondes.
neoteknic
19

Tous les navigateurs ne mettent pas les fichiers en cache avec '?'en elle. Ce que j'ai fait pour m'assurer qu'il était mis en cache autant que possible, j'ai inclus la version dans le nom du fichier.

Donc au lieu de stuff.js?123, je l'ai faitstuff_123.js

J'ai utilisé mod_redirect(je pense) en apache have stuff_*.jspour allerstuff.js

Echo dit de réintégrer Monica
la source
Pourriez-vous expliquer ce que vous avez fait dans .htaccess avec mod_redirect?
Venkat D.
3
Une explication détaillée de cette méthode peut être trouvée sur particletree.com/notebook/…
Karl Bartel
3
Ce serait formidable si vous pouviez inclure votre .htaccesscode dans votre réponse pour référence future.
Fizzix
1
Quels navigateurs ne mettent pas les fichiers en cache avec "?" dedans?
rosell.dk
13

Pour les pages ASP.NET, j'utilise les éléments suivants

AVANT

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

APRÈS (forcer le rechargement)

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

L'ajout de DateTime.Now.Ticks fonctionne très bien.

Ravi Ram
la source
31
Celui-ci va à l'encontre de tout le mécanisme de mise en cache côté client. le paramètre factice devrait être remplacé par quelque chose comme "{version majeure} _ {version_mineure} _ {numéro_ build} _ {révision} qui serait unique pour chaque version.
Tohid
14
Bien qu'il s'agisse probablement d'une solution divine dans un environnement de développement, elle n'est pas adaptée à la production. Cela désactivera entièrement le cache à chaque chargement de la page pour le fichier. Imaginez une charge de 10 000 pages par jour avec un fichier de 50 Ko, cela représente 500 Mo de fichier Javascript sur une base quotidienne.
PhilDulac
@PhilDulac vous pouvez le changer de Ticks pour renvoyer la valeur de chaîne du jour par exemple, ou du mois, ou de la semaine du mois. En fin de compte, cela vous montre simplement comment utiliser l'approche? V
alex
3
@alex Effectivement. Je voulais juste avertir que si l'utilisation démontrée dans la réponse fait son chemin vers la production, elle peut avoir des impacts qui n'apparaissent pas dans le développement.
PhilDulac
2
Un moyen possible de garantir que les nouvelles copies sont chargées une fois par jour consiste à utiliser le type '<script src = "/ Scripts / pages / common.js? Ver <% = DateTime.Now.ToString (" yyyyMMdd ")%>" = "text / javascript"> </script> '. Il est donc chargé une fois en début de journée, puis mis en cache.
Robb Sadler
7

Pour ASP.NET, je suppose que la prochaine solution avec des options avancées (mode debug / release, versions):

Fichiers Js ou Css inclus de cette manière:

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

Global.JsPostfix et Global.CssPostfix est calculé de la manière suivante dans 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
la source
J'utilise .Ticks (voir ma réponse sur cette page)
Ravi Ram
5

De nos jours, la pratique courante consiste à générer un code de hachage de contenu dans le nom de fichier pour forcer le navigateur, en particulier IE, à recharger les fichiers javascript ou les fichiers css.

Par exemple,

vendeur. a7561fb0e9a071baadb9 .js
main. b746e3eb72875af2caa9 .js

C'est généralement le travail des outils de construction tels que webpack. Voici plus de détails si quelqu'un veut essayer si vous utilisez webpack.

le code de schrodinger
la source
4

Si vous générez la page qui établit un lien vers les fichiers JS, une solution simple consiste à ajouter l'horodatage de la dernière modification du fichier aux liens générés.

Ceci est très similaire à la réponse de Huppie, mais fonctionne dans les systèmes de contrôle de version sans substitution de mots clés. C'est aussi mieux que d'ajouter l'heure actuelle, car cela empêcherait la mise en cache même lorsque le fichier ne change pas du tout.


la source
J'aime cette solution, car elle est plus facile à entretenir. Si vous mettez à jour un fichier .js, c'est tout ce que vous devrez faire. Pas besoin de mettre à jour également les références au fichier, car votre code ajoutera automatiquement le dernier horodatage mis à jour.
NL3294
3

La fonction jQuery getScript peut également être utilisée pour garantir qu'un fichier js est bien chargé à chaque fois que la page est chargée.

Voici comment je l'ai fait:

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

Vérifiez la fonction sur http://api.jquery.com/jQuery.getScript/

Par défaut, $ .getScript () définit le paramètre de cache sur false. Cela ajoute un paramètre de requête horodaté à l'URL de la demande pour garantir que le navigateur télécharge le script chaque fois qu'il est demandé.

Michael Franz
la source
8
Nous devons mettre en cache les fichiers si aucun changement ne se produit.
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>

Comment ça fonctionne:

En HTML, écrivez le filepathet le nom comme vous le feriez, mais uniquement dans la fonction. PHP récupère le filetimefichier et renvoie la filepath+name+"?"+timedernière modification

user1944129
la source
3

Nous avons créé un SaaS pour les utilisateurs et leur avons fourni un script à attacher dans la page de leur site Web, et il n'a pas été possible de joindre une version avec le script car l'utilisateur attachera le script à leur site Web pour les fonctionnalités et je ne peux pas les forcer pour changer la version à chaque mise à jour du script

Nous avons donc trouvé un moyen de charger la nouvelle version du script chaque fois que l'utilisateur appelle le script d'origine

le lien du script fourni à l'utilisateur

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

le fichier 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
}

Explication:

L'utilisateur a attaché le script qui lui a été fourni sur son site Web et nous avons vérifié si le jeton unique attaché au script existe ou non à l'aide du sélecteur jQuery et sinon, chargez-le dynamiquement avec un jeton (ou une version) plus récent

Ceci est appelé deux fois le même script, ce qui pourrait être un problème de performances, mais cela résout vraiment le problème de forcer le script à ne pas se charger à partir du cache sans mettre la version dans le lien de script réel donné à l'utilisateur ou au client

Avertissement: Ne pas utiliser si les performances sont un gros problème dans votre cas.

Aman Singh
la source
2

Dans asp.net mvc, vous pouvez utiliser @ DateTime.UtcNow.ToString () pour le numéro de version du fichier js. Le numéro de version change automatiquement avec la date et vous forcez le navigateur des clients à actualiser automatiquement le fichier js. J'utilise cette méthode et cela fonctionne bien.

<script src="~/JsFilePath/[email protected]()"></script>
dragonal
la source
Comme pour les autres solutions suggérées, cela n'entraînera jamais la mise en cache du fichier, ce qui n'est généralement pas souhaitable. Tant qu'aucune modification n'a été apportée au fichier, vous souhaitez probablement que le client utilise la version en cache plutôt que de télécharger à nouveau le fichier inchangé à chaque fois.
Philip Stratford,
Pour votre raison, vous pouvez utiliser le code ci-dessous, un fichier cache avec le numéro de version <script src = "~/JsFilePath/JsFile.js?v=@GetAppVersionNumber ()"> </script>
dragonal
2

location.reload (true);

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

J'appelle dynamiquement cette ligne de code afin de m'assurer que javascript a été récupéré depuis le serveur Web plutôt que depuis le cache du navigateur afin d'échapper à ce problème.

Megan
la source
L'ajout de onload="location.reload();"à mon formulaire me permet d'obtenir le nouveau JS après un rafraîchissement au lieu de relancer ma page. C'est une solution beaucoup plus élégante. Merci!
ZX9
Merci, pourrait utiliser cela avec une vérification si l'ip est reconnue mais n'a pas été utilisée pour se connecter depuis la dernière mise à jour effectuer cela sur la page d'index après la connexion initiale des utilisateurs.
Fi Horan
onload = "location.reload (true);" Ce qui précède ne fonctionne pas pour moi ( en utilisant flacon et la version actuelle de Chrome) aussi: w3schools.com/jsref/met_loc_reload.asp
aspiringGuru
1

Une solution consiste à ajouter une chaîne de requête avec un horodatage à l'URL lors de la récupération de la ressource. Cela profite du fait qu'un navigateur ne mettra pas en cache les ressources extraites des URL contenant des chaînes de requête.

Vous ne voulez probablement pas que le navigateur ne mette pas en cache ces ressources; il est plus probable que vous souhaitiez les mettre en cache, mais vous souhaitez que le navigateur récupère une nouvelle version du fichier lorsqu'elle sera disponible.

La solution la plus courante semble être d'incorporer un horodatage ou un numéro de révision dans le nom du fichier lui-même. C'est un peu plus de travail, car votre code doit être modifié pour demander les bons fichiers, mais cela signifie que, par exemple, la version 7 de votre snazzy_javascript_file.js(ie snazzy_javascript_file_7.js) est mise en cache sur le navigateur jusqu'à ce que vous publiez la version 8, puis votre code passe à chercher à la snazzy_javascript_file_8.jsplace.

Richard Turner
la source
1

L'avantage d'utiliser un file.js?V=1sur un fileV1.jsest que vous n'avez pas besoin de stocker plusieurs versions des fichiers JavaScript sur le serveur.

Le problème que je vois file.js?V=1est que vous pouvez avoir du code dépendant dans un autre fichier JavaScript qui se casse lors de l'utilisation de la nouvelle version des utilitaires de bibliothèque.

Pour des raisons de compatibilité descendante, je pense qu'il est préférable d'utiliser jQuery.1.3.js pour vos nouvelles pages et de laisser les pages existantes utiliser jQuery.1.1.js, jusqu'à ce que vous soyez prêt à mettre à niveau les anciennes pages, si nécessaire.


la source
1

Utilisez une GETvariable de version pour empêcher la mise en cache du navigateur.

L'ajout ?v=AUTO_INCREMENT_VERSIONà la fin de votre URL empêche la mise en cache du navigateur - en évitant tous les scripts mis en cache.

Derek Adair
la source
1

Mon collègue vient de trouver une référence à cette méthode juste après avoir posté (en référence à css) à http://www.stefanhayden.com/blog/2006/04/03/css-caching-hack/ . C'est bien de voir que d'autres l'utilisent et cela semble fonctionner. Je suppose à ce stade qu'il n'y a pas de meilleur moyen que find-replace pour incrémenter ces "numéros de version" dans toutes les balises de script?

AdamB
la source
1

Bien qu'il soit spécifique au framework, Django 1.4 a cette fonctionnalité qui fonctionne de manière similaire au lien vers le site 'greenfelt' dans la réponse ci - dessus

Trent
la source
1

Le contournement du cache dans ASP.NET Core via un assistant de balise traitera cela pour vous et permettra à votre navigateur de conserver les scripts / css mis en cache jusqu'à ce que le fichier change. Ajoutez simplement le tag helper asp-append-version = "true" à votre script (js) ou lien (css):

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

Dave Paquette a un bon exemple et une explication de cache busting ici (bas de page) Cache Busting

ccherwin
la source
Cela ne fonctionne-t-il pas dans ASP.NET normal? J'ai essayé d'ajouter la version asp-append à ma balise de script et tout ce que le navigateur voit est la balise de script exactement telle qu'elle apparaît dans la source, y compris l'attribut asp-append-version.
tolsen64
Il s'agit d'un attribut .NET Core associé aux Tag Helpers. Il ajoute le nom du script avec une version pour que le serveur / navigateur voit toujours la dernière version et les derniers téléchargements
ccherwin
0

La solution la plus simple? Ne laissez pas le cache du navigateur du tout. Ajoutez l'heure actuelle (en ms) en tant que requête.

(Vous êtes toujours en version bêta, vous pouvez donc faire un argument raisonnable pour ne pas optimiser les performances. Mais YMMV ici.)

pcorcoran
la source
13
À mon humble avis, c'est une mauvaise solution. Que faire si vous n'êtes pas en version bêta et que vous publiez une mise à jour importante?
d -_- b
0

Une façon simple. Modifier 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
la source
Cela provoque une redirection qui est une solution sous-optimale mais fonctionnelle.
2
0

Ci-dessous a fonctionné pour moi:

<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
la source