RequireJS semble faire quelque chose en interne qui met en cache les fichiers JavaScript requis. Si je modifie l'un des fichiers requis, je dois le renommer pour que les modifications soient appliquées.
L'astuce courante consistant à ajouter un numéro de version en tant que paramètre de chaîne de requête à la fin du nom de fichier ne fonctionne pas avec requirejs <script src="jsfile.js?v2"></script>
Ce que je recherche, c’est un moyen d’empêcher la mise en cache interne des scripts requis RequireJS sans avoir à renommer mes fichiers de script à chaque mise à jour.
Solution multiplate-forme:
J'utilise maintenant urlArgs: "bust=" + (new Date()).getTime()
pour la suppression automatique du cache pendant le développement et urlArgs: "bust=v2"
pour la production, où j'incrémente la version codée en dur après le déploiement d'un script requis mis à jour.
Remarque:
@Dustin Getz a mentionné dans une réponse récente que les outils de développement Chrome vont supprimer les points d'arrêt lors du débogage lorsque les fichiers Javascript sont continuellement actualisés. Une solution de contournement consiste à écrire debugger;
dans le code pour déclencher un point d'arrêt dans la plupart des débogueurs Javascript.
Solutions spécifiques au serveur:
Pour obtenir des solutions spécifiques qui pourraient mieux fonctionner pour votre environnement de serveur, tel que Node ou Apache, reportez-vous à certaines des réponses ci-dessous.
RequireJS peut être configuré pour ajouter une valeur à chacune des URL de script pour le contournement du cache.
Dans la documentation RequireJS ( http://requirejs.org/docs/api.html#config ):
urlArgs: arguments de chaîne de requête supplémentaires ajoutés aux URL RequireJS utilise pour récupérer des ressources. Le plus utile pour mettre en cache le buste lorsque le navigateur ou le serveur n'est pas configuré correctement.
Exemple, ajout de "v2" à tous les scripts:
require.config({
urlArgs: "bust=v2"
});
À des fins de développement, vous pouvez forcer RequireJS à contourner le cache en ajoutant un horodatage:
require.config({
urlArgs: "bust=" + (new Date()).getTime()
});
Exiger des chargements de script respectant les en-têtes de mise en cache http. (Les scripts sont chargés avec un <script>
inséré de manière dynamique, ce qui signifie que la demande ressemble à tout ancien actif chargé.)
Servez vos ressources javascript avec les en-têtes HTTP appropriés pour désactiver la mise en cache pendant le développement.
L'utilisation de l'urlArgs de require signifie que les points d'arrêt que vous définissez ne seront pas conservés lors des actualisations. vous finissez par avoir besoin de mettre des déclarations debugger
partout dans votre code. Mal. J'utilise urlArgs
pour les ressources de contournement de cache lors des mises à niveau de production avec git sha; je peux ensuite configurer mes ressources pour qu'elles soient mises en cache pour toujours et avoir la garantie de ne jamais avoir d'actifs périmés.
En développement, je simule toutes les requêtes ajax avec une configuration complexe mockjax , puis je peux servir mon application en mode javascript uniquement avec un serveur http python 10 lignes avec toute la mise en cache désactivée . Pour moi, cela a été étendu à une application "d'entreprise" assez volumineuse avec des centaines de points de terminaison Webservice reposants. Nous avons même un concepteur sous contrat qui peut travailler avec notre véritable base de code de production sans lui donner accès à notre code de base.
La solution urlArgs a des problèmes. Malheureusement, vous ne pouvez pas contrôler tous les serveurs proxy pouvant se trouver entre vous et le navigateur Web de votre utilisateur. Certains de ces serveurs proxy peuvent malheureusement être configurés pour ignorer les paramètres d'URL lors de la mise en cache de fichiers. Si cela se produit, la version incorrecte de votre fichier JS sera remise à votre utilisateur.
J'ai finalement abandonné et mis en œuvre mon propre correctif directement dans require.js. Si vous souhaitez modifier votre version de la bibliothèque requirejs, cette solution pourrait fonctionner pour vous.
Vous pouvez voir le patch ici:
https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eee631ce68eee09a5a67
Une fois ajouté, vous pouvez faire quelque chose comme ceci dans votre config require:
var require = {
baseUrl: "/scripts/",
cacheSuffix: ".buildNumber"
}
Utilisez votre système de génération ou votre environnement de serveur pour remplacer buildNumber
par un identifiant de révision/version de logiciel/couleur préférée.
Utiliser require comme ceci:
require(["myModule"], function() {
// no-op;
});
Causera besoin de demander ce fichier:
http://yourserver.com/scripts/myModule.buildNumber.js
Sur notre environnement de serveur, nous utilisons des règles de réécriture d’URL pour extraire le numéro de construction et servir le fichier JS approprié. De cette façon, nous n'avons pas à nous soucier de renommer tous nos fichiers JS.
Le correctif ignorera tout script spécifiant un protocole et n'affectera pas les fichiers autres que JS.
Cela fonctionne bien pour mon environnement, mais je me rends compte que certains utilisateurs préfèrent un préfixe plutôt qu'un suffixe, il devrait être facile de modifier mon engagement en fonction de vos besoins.
Mettre à jour:
Dans la discussion sur la demande d'extraction, l'auteur de requirejs suggère que cela pourrait fonctionner comme solution pour préfixer le numéro de révision:
var require = {
baseUrl: "/scripts/buildNumber."
};
Je n'ai pas essayé cela, mais cela impliquerait que l'URL suivante soit demandée:
http://yourserver.com/scripts/buildNumber.myModule.js
Ce qui pourrait très bien fonctionner pour beaucoup de gens qui peuvent utiliser un préfixe.
Voici quelques questions possibles en double:
RequireJS et mise en cache du proxy
require.js - Comment puis-je définir une version des modules requis dans l'URL?
Inspiré par Expire le cache sur require.js data-main nous avons mis à jour notre script de déploiement avec la tâche ant suivante:
<target name="deployWebsite">
<untar src="${temp.dir}/website.tar.gz" dest="${website.dir}" compression="gzip" />
<!-- fetch latest buildNumber from build agent -->
<replace file="${website.dir}/js/main.js" token="@Revision@" value="${buildNumber}" />
</target>
Où le début de main.js ressemble à:
require.config({
baseUrl: '/js',
urlArgs: 'bust=@Revision@',
...
});
urlArgs
peut causer des problèmes!
L’auteur principal de requirejs préfère ne pas utiliser urlArgs
:
Pour les actifs déployés, je préfère mettre la version ou le hachage pour l'ensemble construire en tant que répertoire de construction, puis modifiez simplement la configuration
baseUrl
utilisée pour que le projet utilise ce répertoire versionné en tant quebaseUrl
. Ensuite aucun autre fichier ne change, et cela permet d'éviter certains problèmes de proxy où ils ne peut pas mettre en cache une URL avec une chaîne de requête.
[Styling mine.]
Je suis ce conseil.
Je préfère utiliser un serveur qui met intelligemment en cache les fichiers susceptibles de changer fréquemment: un serveur qui émet Last-Modified
et répond à If-Modified-Since
avec 304 si nécessaire. Même un serveur basé sur le paramètre express de Node pour servir des fichiers statiques le fait immédiatement. Cela ne nécessite aucune intervention de mon navigateur et ne gâche pas les points d'arrêt.
J'ai pris cet extrait de AskApache et je l'ai mis dans un fichier .conf distinct de mon serveur Web Apache local (dans mon cas, /etc/Apache2/others/preventcaching.conf):
<FilesMatch "\.(html|htm|js|css)$">
FileETag None
<ifModule mod_headers.c>
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</ifModule>
</FilesMatch>
Pour le développement, cela fonctionne bien sans qu'il soit nécessaire de changer le code. En ce qui concerne la production, je pourrais utiliser l’approche de @dvtoever.
Correctif rapide pour le développement
Pour le développement, vous pouvez simplement désactiver le cache dans Chrome Dev Tools ( Désactiver le cache Chrome pour le développement de sites Web ). La désactivation du cache ne se produit que si la boîte de dialogue des outils de développement est ouverte. Vous n'avez donc pas à craindre de basculer cette option chaque fois que vous naviguez régulièrement.
Remarque: Utiliser 'urlArgs' est la solution appropriée en production pour que les utilisateurs obtiennent le code le plus récent. Mais cela rend le débogage difficile car chrome invalide les points d'arrêt à chaque actualisation (car c'est un «nouveau» fichier servi à chaque fois).
Je ne recommande pas d'utiliser ' urlArgs ' pour la mise en mémoire cache avec RequireJS. Comme cela ne résout pas complètement le problème. La mise à jour d'une version no entraînera le téléchargement de toutes les ressources, même si vous ne modifiez qu'une seule ressource.
Pour gérer ce problème, je recommande d'utiliser des modules Grunt tels que 'filerev' pour créer la révision no. En plus de cela, j'ai écrit une tâche personnalisée dans Gruntfile pour mettre à jour la révision non, si nécessaire.
Si nécessaire, je peux partager l'extrait de code pour cette tâche.
Voici comment je le fais dans Django/Flask (peut être facilement adapté à d'autres langages/systèmes VCS):
Dans votre config.py
(je l’utilise en python3, vous devrez peut-être modifier l’encodage en python2)
import subprocess
GIT_HASH = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip().decode('utf-8')
Puis dans votre modèle:
{% if config.DEBUG %}
require.config({urlArgs: "bust=" + (new Date().getTime())});
{% else %}
require.config({urlArgs: "bust=" + {{ config.GIT_HASH|tojson }}});
{% endif %}
git rev-parse HEAD
une fois au démarrage de l'application et la stocke dans l'objet config
Solution dynamique (sans urlArgs)
Il existe une solution simple à ce problème, vous permettant de charger un numéro de révision unique pour chaque module.
Vous pouvez enregistrer la fonction requirejs.load d'origine, l'écraser avec votre propre fonction et analyser à nouveau l'URL modifiée avec le requirejs.load d'origine:
var load = requirejs.load;
requirejs.load = function (context, moduleId, url) {
url += "?v=" + oRevision[moduleId];
load(context, moduleId, url);
};
Dans notre processus de construction, j’ai utilisé "gulp-rev" pour créer un fichier manifeste contenant toutes les révisions de tous les modules utilisés. Version simplifiée de ma tâche gulp:
gulp.task('gulp-revision', function() {
var sManifestFileName = 'revision.js';
return gulp.src(aGulpPaths)
.pipe(rev())
.pipe(rev.manifest(sManifestFileName, {
transformer: {
stringify: function(a) {
var oAssetHashes = {};
for(var k in a) {
var key = (k.substr(0, k.length - 3));
var sHash = a[k].substr(a[k].indexOf(".") - 10, 10);
oAssetHashes[key] = sHash;
}
return "define([], function() { return " + JSON.stringify(oAssetHashes) + "; });"
}
}
}))
.pipe(gulp.dest('./'));
});
cela générera un module AMD avec des numéros de révision à moduleNames, inclus en tant que "oRevision" dans le fichier main.js, où vous écraserez la fonction requirejs.load comme indiqué précédemment.