caching - problem - force cache refresh javascript
¿Cómo evitar el caché del navegador en el sitio de Angular 2? (5)
En cada plantilla html solo agrego las siguientes metaetiquetas en la parte superior:
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
Según tengo entendido, cada plantilla es independiente, por lo tanto, no hereda la configuración de reglas de meta no almacenamiento en caché en el archivo index.html.
Actualmente estamos trabajando en un nuevo proyecto con actualizaciones periódicas que uno de nuestros clientes usa a diario. Este proyecto se está desarrollando utilizando angular 2 y enfrentamos problemas de caché, es decir, nuestros clientes no están viendo los últimos cambios en sus máquinas.
Principalmente, los archivos html / css para los archivos js parecen actualizarse correctamente sin causar muchos problemas.
Encontró una manera de hacer esto, simplemente agregue una cadena de consulta para cargar sus componentes, así:
@Component({
selector: ''some-component'',
templateUrl: `./app/component/stuff/component.html?v=${new Date().getTime()}`,
styleUrls: [`./app/component/stuff/component.css?v=${new Date().getTime()}`]
})
Esto debería obligar al cliente a cargar la copia del servidor de la plantilla en lugar de la del navegador. Si desea que se actualice solo después de un cierto período de tiempo, puede usar esta ISOString en su lugar:
new Date().toISOString() //2016-09-24T00:43:21.584Z
Y subcadena algunos caracteres para que solo cambien después de una hora, por ejemplo:
new Date().toISOString().substr(0,13) //2016-09-24T00
Espero que esto ayude
Puede controlar el caché del cliente con encabezados HTTP. Esto funciona en cualquier marco web.
Puede configurar las directivas de estos encabezados para que tengan un control detallado sobre cómo y cuándo habilitar | deshabilitar la memoria caché:
-
Cache-Control
-
Surrogate-Control
-
Expires
-
ETag
(muy buena) -
Pragma
(si desea admitir navegadores antiguos)
El buen almacenamiento en caché es bueno, pero muy complejo, en todos los sistemas informáticos . Eche un vistazo a https://helmetjs.github.io/docs/nocache/#the-headers para obtener más información.
Tuve un problema similar con el index.html almacenado en caché por el navegador o más complicado por medio cdn / proxies (F5 no lo ayudará).
Busqué una solución que verifique al 100% que el cliente tiene la última versión index.html, por suerte encontré esta solución por Henrik Peinar:
https://blog.nodeswat.com/automagic-reload-for-clients-after-deploy-with-angular-4-8440c9fdd96c
La solución también resuelve el caso en el que el cliente permanece con el navegador abierto durante días, el cliente busca actualizaciones en intervalos y vuelve a cargar si se implementa una versión más nueva.
La solución es un poco complicada pero funciona de maravilla:
-
use el hecho de que
ng cli -- prod
produce archivos hash con uno de ellos llamado main. [hash] .js - crear un archivo version.json que contenga ese hash
- cree un servicio angular VersionCheckService que verifique version.json y vuelva a cargar si es necesario.
- Tenga en cuenta que una secuencia de comandos js que se ejecuta después de la implementación crea para usted version.json y reemplaza el hash en el servicio angular, por lo que no se necesita trabajo manual, sino que se ejecuta post-build.js
Dado que la solución de Henrik Peinar fue para angular 4, hubo cambios menores, coloco también los scripts fijos aquí:
VersionCheckService:
import { Injectable } from ''@angular/core'';
import { HttpClient } from ''@angular/common/http'';
@Injectable()
export class VersionCheckService {
// this will be replaced by actual hash post-build.js
private currentHash = ''{{POST_BUILD_ENTERS_HASH_HERE}}'';
constructor(private http: HttpClient) {}
/**
* Checks in every set frequency the version of frontend application
* @param url
* @param {number} frequency - in milliseconds, defaults to 30 minutes
*/
public initVersionCheck(url, frequency = 1000 * 60 * 30) {
//check for first time
this.checkVersion(url);
setInterval(() => {
this.checkVersion(url);
}, frequency);
}
/**
* Will do the call and check if the hash has changed or not
* @param url
*/
private checkVersion(url) {
// timestamp these requests to invalidate caches
this.http.get(url + ''?t='' + new Date().getTime())
.subscribe(
(response: any) => {
const hash = response.hash;
const hashChanged = this.hasHashChanged(this.currentHash, hash);
// If new version, do something
if (hashChanged) {
// ENTER YOUR CODE TO DO SOMETHING UPON VERSION CHANGE
// for an example: location.reload();
// or to ensure cdn miss: window.location.replace(window.location.href + ''?rand='' + Math.random());
}
// store the new hash so we wouldn''t trigger versionChange again
// only necessary in case you did not force refresh
this.currentHash = hash;
},
(err) => {
console.error(err, ''Could not get version'');
}
);
}
/**
* Checks if hash has changed.
* This file has the JS hash, if it is a different one than in the version.json
* we are dealing with version change
* @param currentHash
* @param newHash
* @returns {boolean}
*/
private hasHashChanged(currentHash, newHash) {
if (!currentHash || currentHash === ''{{POST_BUILD_ENTERS_HASH_HERE}}'') {
return false;
}
return currentHash !== newHash;
}
}
cambiar a 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);
}
}
}
El script posterior a la compilación que hace la magia, post-build.js:
const path = require(''path'');
const fs = require(''fs'');
const util = require(''util'');
// get application version from package.json
const appVersion = require(''../package.json'').version;
// promisify core API''s
const readDir = util.promisify(fs.readdir);
const writeFile = util.promisify(fs.writeFile);
const readFile = util.promisify(fs.readFile);
console.log(''/nRunning post-build tasks'');
// our version.json will be in the dist folder
const versionFilePath = path.join(__dirname + ''/../dist/version.json'');
let mainHash = '''';
let mainBundleFile = '''';
// RegExp to find main.bundle.js, even if it doesn''t include a hash in it''s name (dev build)
let mainBundleRegexp = /^main.?([a-z0-9]*)?.js$/;
// read the dist folder files and find the one we''re looking for
readDir(path.join(__dirname, ''../dist/''))
.then(files => {
mainBundleFile = files.find(f => mainBundleRegexp.test(f));
if (mainBundleFile) {
let matchHash = mainBundleFile.match(mainBundleRegexp);
// if it has a hash in it''s name, mark it down
if (matchHash.length > 1 && !!matchHash[1]) {
mainHash = matchHash[1];
}
}
console.log(`Writing version and hash to ${versionFilePath}`);
// write current version and hash into the version.json file
const src = `{"version": "${appVersion}", "hash": "${mainHash}"}`;
return writeFile(versionFilePath, src);
}).then(() => {
// main bundle file not found, dev build?
if (!mainBundleFile) {
return;
}
console.log(`Replacing hash in the ${mainBundleFile}`);
// replace hash placeholder in our main.js file so the code knows it''s current hash
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(''Error with post build:'', err);
});
simplemente coloque el script en la carpeta de
compilación
(nueva) ejecute el script usando el
node ./build/post-build.js
después de
node ./build/post-build.js
carpeta dist usando
ng build --prod
angular-cli
resuelve esto de manera brillante al proporcionar un
--output-hashing
para el comando de
build
.
Ejemplo de uso:
ng build --aot --output-hashing=all
Bundling & Tree-Shaking
proporciona algunos detalles y contexto.
Ejecutando
ng help build
, documenta la bandera:
--output-hashing=none|all|media|bundles (String) Define the output filename cache-busting hashing mode.
aliases: -oh <value>, --outputHashing <value>
Aunque esto solo es aplicable a los usuarios de angular-cli , funciona de manera brillante y no requiere ningún cambio de código ni herramientas adicionales.