J'ai eu un problème similaire avec le fichier index.html étant mis en cache par le navigateur ou plus difficilement par des cdn/proxies intermédiaires (F5 ne vous aidera pas).
J'ai cherché une solution qui vérifie à 100% que le client a la version la plus récente de index.html, heureusement j'ai trouvé cette solution par Henrik Peinar :
https://blog.nodeswat.com/automagic-reload-for-clients-after-deploy-with-angular-4-8440c9fdd96c
La solution résout également le cas où le client reste avec le navigateur ouvert pendant des jours, le client vérifie les mises à jour à intervalles et recharge si une version plus récente est déployée.
La solution est un peu astucieuse mais fonctionne à merveille :
- utilisez le fait que
ng cli -- prod
produit des fichiers hachés avec l'un d'eux appelé main.[hash].js
- créez un fichier version.json contenant ce hachage
- créez un service Angular VersionCheckService qui vérifie version.json et recharge si nécessaire.
- Notez qu'un script js s'exécutant après le déploiement crée pour vous à la fois version.json et remplace le hachage dans le service Angular, donc aucun travail manuel n'est nécessaire, mais exécutez post-build.js.
Puisque la solution de Henrik Peinar était pour Angular 4, il y a eu des changements mineurs, je place également les scripts corrigés ici :
VersionCheckService :
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class VersionCheckService {
// ce sera remplacé par le hachage réel post-build.js
private currentHash = '{{POST_BUILD_ENTERS_HASH_HERE}}';
constructor(private http: HttpClient) {}
/**
* Vérifie à une fréquence définie la version de l'application frontend
* @param url
* @param {number} frequency - en millisecondes, par défaut à 30 minutes
*/
public initVersionCheck(url, frequency = 1000 * 60 * 30) {
// vérifie la première fois
this.checkVersion(url);
setInterval(() => {
this.checkVersion(url);
}, frequency);
}
/**
* Fait l'appel et vérifie si le hachage a changé ou non
* @param url
*/
private checkVersion(url) {
// horodatage de ces requêtes pour invalider les caches
this.http.get(url + '?t=' + new Date().getTime())
.subscribe(
(response: any) => {
const hash = response.hash;
const hashChanged = this.hasHashChanged(this.currentHash, hash);
// Si nouvelle version, faire quelque chose
if (hashChanged) {
// ENTREZ VOTRE CODE POUR FAIRE QUELQUE CHOSE EN CAS DE CHANGEMENT DE VERSION
// par exemple : location.reload();
// ou pour garantir un manque de cdn : window.location.replace(window.location.href + '?rand=' + Math.random());
}
// enregistrer le nouveau hachage pour ne pas déclencher de nouveau le changement de version
// uniquement nécessaire si vous n'avez pas forcé le rafraîchissement
this.currentHash = hash;
},
(err) => {
console.error(err, 'Impossible de récupérer la version');
}
);
}
/**
* Vérifie si le hachage a changé.
* Ce fichier contient le hachage JS, s'il est différent de celui dans le version.json
* nous sommes en présence d'un changement de version
* @param currentHash
* @param newHash
* @returns {boolean}
*/
private hasHashChanged(currentHash, newHash) {
if (!currentHash || currentHash === '{{POST_BUILD_ENTERS_HASH_HERE}}') {
return false;
}
return currentHash !== newHash;
}
}
changer dans le AppComponent principal :
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
constructor(private versionCheckService: VersionCheckService) {
}
ngOnInit() {
console.log('AppComponent.ngOnInit() environment.versionCheckUrl=' + environment.versionCheckUrl);
if (environment.versionCheckUrl) {
this.versionCheckService.initVersionCheck(environment.versionCheckUrl);
}
}
}
Le script post-build qui fait de la magie, post-build.js :
const path = require('path');
const fs = require('fs');
const util = require('util');
// obtenir la version de l'application à partir de package.json
const appVersion = require('../package.json').version;
// promisifier les API's de base
const readDir = util.promisify(fs.readdir);
const writeFile = util.promisify(fs.writeFile);
const readFile = util.promisify(fs.readFile);
console.log('\nExécution des tâches post-build');
// notre version.json sera dans le dossier dist
const versionFilePath = path.join(__dirname + '/../dist/version.json');
let mainHash = '';
let mainBundleFile = '';
// RegExp pour trouver main.bundle.js, même s'il n'inclut pas de hachage dans son nom (construction de développement)
let mainBundleRegexp = /^main.?([a-z0-9]*)?.js$/;
// lire les fichiers du dossier dist et trouver celui que nous cherchons
readDir(path.join(__dirname, '../dist/'))
.then(files => {
mainBundleFile = files.find(f => mainBundleRegexp.test(f));
if (mainBundleFile) {
let matchHash = mainBundleFile.match(mainBundleRegexp);
// s'il a un hachage dans son nom, le marquer
if (matchHash.length > 1 && !!matchHash[1]) {
mainHash = matchHash[1];
}
}
console.log(`Écriture de la version et du hachage dans ${versionFilePath}`);
// écrire la version actuelle et le hachage dans le fichier version.json
const src = `{"version": "${appVersion}", "hash": "${mainHash}"}`;
return writeFile(versionFilePath, src);
}).then(() => {
// fichier du bundle principal non trouvé, construction de développement ?
if (!mainBundleFile) {
return;
}
console.log(`Remplacement du hachage dans le ${mainBundleFile}`);
// remplacer le marqueur de hachage dans notre fichier main.js pour que le code connaisse son hachage actuel
const mainFilepath = path.join(__dirname, '../dist/', mainBundleFile);
return readFile(mainFilepath, 'utf8')
.then(mainFileData => {
const replacedFile = mainFileData.replace('{{POST_BUILD_ENTERS_HASH_HERE}}', mainHash);
return writeFile(mainFilepath, replacedFile);
});
}).catch(err => {
console.log('Erreur avec la construction post :', err);
});
placez simplement le script dans le nouveau dossier build exécutez le script en utilisant node ./build/post-build.js
après avoir construit le dossier dist en utilisant ng build --prod
3 votes
Très bonne question. J'ai le même problème. Quel est le meilleur moyen de résoudre ce problème ? Est-ce possible avec gulp ou tout outil similaire pour publier une application Angular 2 ?
2 votes
@jump4791 La meilleure façon est d'utiliser webpack et de compiler le projet en utilisant les paramètres de production. Je suis actuellement en train d'utiliser ce dépôt, il suffit de suivre les étapes et tout devrait bien se passer : github.com/AngularClass/angular2-webpack-starter
0 votes
Je rencontre également le même problème.
3 votes
Je sais que c'est une vieille question mais je voulais ajouter la solution que j'ai trouvée, pour ceux qui tomberaient dessus. Lors de la construction avec
ng build
, l'ajout de la balise-prod
ajoute un hachage aux noms de fichiers générés. Cela force le rechargement de tout saufindex.html
. Ce post github avait quelques astuces sur la façon de faire recharger cela.2 votes
Index.html est la cause principale. Parce qu'il n'a pas de code de hachage, quand il est mis en cache, tout le reste est utilisé depuis le cache.