AngularJS disabilita la memorizzazione nella cache parziale sulla macchina di sviluppo


210

Ho un problema con la memorizzazione nella cache dei parziali in AngularJS.

Nella mia pagina HTML ho:

<body>
 <div ng-view></div>
<body>

dove sono caricati i miei parziali.

Quando cambio codice HTML nel mio parziale, il browser carica ancora i vecchi dati.

C'è qualche soluzione?


4
Solo una breve nota: ho avuto un problema con questo che era più correlato alle intestazioni di controllo della cache che la mia applicazione Flask stava restituendo. Ho risolto il problema aggiungendo app.config.update(SEND_FILE_MAX_AGE_DEFAULT=0)al mio flask_app.py. (Immagino che cose simili esistano per altri server Web).
gatoatigrado,

4
Se stai usando Chrome, fai semplicemente un Ctrl+Shift+R(ad esempio Ricarica dura) e non importa quale meccanismo di memorizzazione nella cache sia usato, Chrome lo ignorerà e
recupererà di

4
ctrl + shift + R non funziona per me in Chrome, ma nella scheda "rete" degli strumenti di sviluppo, facendo clic su "disabilita cache" funziona perfettamente. Per me, questo è un problema lato client che non dovrebbe essere risolto usando hack sul server come molti dei suggerimenti di seguito; dovrebbe essere risolto sul client in cui esiste il "problema". Se lo si corregge sul server e si dimentica di annullarlo, la produzione potrebbe essere influenzata negativamente.
Ant Kutschera,

8
ctrl + maiusc + R ignora la cache per le normali richieste. richieste Ajax fatte da angolare per ng-include| ng-view| templateUrlnon sono gestiti da questa scorciatoia
André Werlang,

2
Non puoi chiedere a tutti gli utenti finali di Ctrl + Maiusc + R quando visitano il sito, quindi qual è la risposta a questa domanda per il caso di non sviluppo? "Per me, questo è un problema lato client che non dovrebbe essere risolto usando gli hack sul server come molti dei suggerimenti di seguito" - Non sono d'accordo, non puoi controllare i client in un ambiente web, quindi la correzione per la produzione deve essere guidato dall'applicazione. Per questo motivo ho accettato: $ rootScope. $ On ('$ viewContentLoaded', function () {$ templateCache.removeAll ();});
Robert Christian,

Risposte:


200

Per lo sviluppo puoi anche disattivare la cache del browser: in Chrome Dev Tools in basso a destra fai clic sull'ingranaggio e seleziona l'opzione

Disabilita la cache (mentre DevTools è aperto)

Aggiornamento: in Firefox esiste la stessa opzione in Debugger -> Impostazioni -> Sezione avanzata (selezionata per la versione 33)

Aggiornamento 2: sebbene questa opzione appaia in Firefox alcuni riportano che non funziona. Suggerisco di usare firebug e seguire la risposta di hadaytullah.


7
Questa dovrebbe essere la risposta accettata perché non richiede una modifica del codice ed è più che radicale alla richiesta del PO. Ovviamente si vorrebbe che un'app di produzione memorizzasse nella cache le richieste, quindi fare ciò che suggeriva la gente sopra, benché ben intenzionato, potrebbe rivelarsi problematico se il codice fosse lasciato in un'app di produzione.
Aaron Wagner,

Qualche suggerimento per gli altri browser come Firefox?
Lereveme,

In Firefox: Debugger> Impostazioni (l'ingranaggio) c'è la stessa opzione.
LukeSolar,

5
Questo non funziona in Firefox. Anche quando la cache è disabilitata e la casella degli strumenti è aperta, i modelli sono ancora memorizzati nella cache.
Patrick J Collins,

4
Anche la memorizzazione nella cache influisce sulla produzione? Cosa succede se invio nuovi file Web al server, cosa impedisce alle richieste successive dei client di produzione di caricare una versione cache pubblicata?
Meffect,

111

Basandoci un po 'sulla risposta di @ Valentyn, ecco un modo per cancellare sempre automaticamente la cache ogni volta che cambia il contenuto di ng-view:

myApp.run(function($rootScope, $templateCache) {
   $rootScope.$on('$viewContentLoaded', function() {
      $templateCache.removeAll();
   });
});

@ user252690 Probabilmente devi anche assicurarti che il modello html non sia stato inviato con le intestazioni della cache. Vedi qui e qui per possibili correzioni
Chris Foster

30
Un piccolo avvertimento: svuotare $ templateCache potrebbe effettivamente avere conseguenze indesiderate. Ad esempio, UI Bootstrap aggiunge i parziali predefiniti direttamente a $ templateCache durante l'inizializzazione e in seguito si aspetta che siano presenti.
Strille,

@Strille Stavo cercando di usare Angular-ui-bootstrap Modal. Il pop-up non era visibile. perché $ templateCache.removeAll (); qualche soluzione per questo?
Mukun,

4
@Mukun: Non c'è una soluzione semplice nel modo in cui la vedo io, a parte il non usare removeAll () ma invece usa semplicemente remove () per cancellare le chiavi che devi cancellare. Avresti bisogno di una sorta di contabilità per sapere quali chiavi eliminare.
Strille,

esiste un modo per svuotare la cache solo per una specifica cache di visualizzazione dell'interfaccia utente.
Gayan,

37

Come menzionato nelle altre risposte, qui e qui , la cache può essere cancellata usando:

$templateCache.removeAll();

Tuttavia, come suggerito da gatoatigrado nel commento , questo sembra funzionare solo se il modello html è stato offerto senza intestazioni di cache.

Quindi questo funziona per me:

In angolare:

app.run(['$templateCache', function ( $templateCache ) {
    $templateCache.removeAll(); }]);

Puoi aggiungere le intestazioni della cache in vari modi, ma qui ci sono un paio di soluzioni che funzionano per me.

Se si utilizza IIS, aggiungere questo al tuo web.config:

<location path="scripts/app/views">
  <system.webServer>
    <staticContent>
      <clientCache cacheControlMode="DisableCache" />
    </staticContent>
  </system.webServer>
</location>

Se usi Nginx, puoi aggiungerlo alla tua configurazione:

location ^~ /scripts/app/views/ {
    expires -1;   
}

modificare

Ho appena capito che la domanda menzionava la devmacchina, ma spero che questo possa ancora aiutare qualcuno ...


2
sì, anche se questo non risponde direttamente alla domanda originale, in realtà mi ha aiutato a risolvere il problema di memorizzazione nella cache su un sito Web live.
Andre

31

Se stai parlando della cache che è stata utilizzata per la memorizzazione nella cache dei modelli senza ricaricare l'intera pagina, puoi svuotarla con qualcosa del tipo:

.controller('mainCtrl', function($scope, $templateCache) {
  $scope.clearCache = function() { 
    $templateCache.removeAll();
  }
});

E in markup:

<button ng-click='clearCache()'>Clear cache</button>

E premere questo pulsante per cancellare la cache.


22

Soluzione per Firefox (33.1.1) utilizzando Firebug (22.0.6)

  1. Strumenti> Strumenti Web> Firebug> Apri Firebug.
  2. Nelle viste Firebug vai alla vista "Rete".
  3. Un simbolo del menu a discesa apparirà accanto a "Rete" (titolo della vista).
  4. Seleziona "Disabilita cache del browser" dal menu a discesa.

Ho provato su Firebug (2.0.11) e Firefox (38.0.1) su Mac ma questo non ha funzionato.
paullb,

19

Questo frammento mi ha aiutato a sbarazzarmi della cache dei template

app.run(function($rootScope, $templateCache) {
    $rootScope.$on('$routeChangeStart', function(event, next, current) {
        if (typeof(current) !== 'undefined'){
            $templateCache.remove(current.templateUrl);
        }
    });
});

I dettagli del seguente frammento sono disponibili su questo link: http://oncodesign.io/2014/02/19/safely-prevent-template-caching-in-angularjs/


nitpick ma quando la corrente non è mai un oggetto? Non sono nemmeno sicuro che non ci sia mai, maif (!current) { return; }
Nate-Wilkins,

Ciò sconfigge la memorizzazione nella cache di modelli basati sul percorso di Angular, ma non i parziali ng-include.
bradw2k,

16

Sto pubblicando questo solo per coprire tutte le possibilità poiché nessuna delle altre soluzioni ha funzionato per me (hanno generato errori dovuti alle dipendenze del modello angolare-bootstrap, tra gli altri).

Durante lo sviluppo / il debug di un modello specifico, puoi assicurarti che si aggiorni sempre includendo un timestamp nel percorso, come questo:

       $modal.open({
          // TODO: Only while dev/debug. Remove later.
          templateUrl: 'core/admin/organizations/modal-selector/modal-selector.html?nd=' + Date.now(),
          controller : function ($scope, $modalInstance) {
            $scope.ok = function () {
              $modalInstance.close();
            };
          }
        });

Nota il finale ?nd=' + Date.now()nella templateUrlvariabile.


1
Successivamente puoi impostare a .value('DEBUG', true)per abilitare quella linea o meno.
Diosney,

1
La mia soluzione era quella di utilizzare il seguente all'interno della fase di inizializzazione del mio modulo principale sotto .run(function($rootScope) { $rootScope.DEBUG = true; ...e poi all'interno della direttiva iniettare il $ rootScope come .directive('filter', ['$rootScope', function($rootScope)...e nell'oggetto-beni restituiti: templateUrl: '/app/components/filter/filter-template.html' + ($rootScope.DEBUG ? '?n=' + Date.now() : ''). Forse potresti elaborare il tuo approccio .value ('DEBUG', true)? Upvoted!
JackLeEmmerdeur

L'uso di .value('DEBUG', trueè lo stesso di quello che hai fatto $rootScope, ma senza ingombrare :) Potresti successivamente iniettare DEBUGnel controller e interrogare come un normale servizio.
Diosney,

Potresti forse estendere il codice sorgente nella tua risposta per includere la .value(...)cosa, se non è troppo sofisticato? Suppongo che il concetto alla base sia una best practice angolare a me sconosciuta.
JackLeEmmerdeur

1
Questa soluzione è molto utile quando si lavora con Ionic. Mi farà risparmiare tanto tempo in quanto rende utile il caricamento del fegato. Grazie mille!
Ajuser

11

Come altri hanno già detto, sconfiggere completamente la cache per scopi di sviluppo può essere fatto facilmente senza cambiare codice: utilizzare un'impostazione del browser o un plug-in. Al di fuori di dev, per annullare la memorizzazione nella cache dei modelli angolari dei modelli basati sul percorso, rimuovere l'URL del modello dalla cache durante $ routeChangeStart (o $ stateChangeStart, per il router dell'interfaccia utente) come ha mostrato Shayan. Tuttavia, ciò NON influisce sulla memorizzazione nella cache dei modelli caricati da ng-include, poiché tali modelli non vengono caricati tramite il router.

Volevo essere in grado di aggiornare qualsiasi modello, inclusi quelli caricati da ng-include, in produzione e fare in modo che gli utenti ricevessero rapidamente l'aggiornamento rapido nel loro browser, senza dover ricaricare l'intera pagina. Inoltre, non mi preoccupo di sconfiggere la cache HTTP per i modelli. La soluzione è intercettare ogni richiesta HTTP che l'app fa, ignorare quelli che non sono per i modelli .html della mia app, quindi aggiungere un parametro all'URL del modello che cambia ogni minuto. Tieni presente che il controllo del percorso è specifico del percorso dei modelli della tua app. Per ottenere un intervallo diverso, modificare la matematica per il parametro o rimuovere completamente% per non ottenere la memorizzazione nella cache.

// this defeats Angular's $templateCache on a 1-minute interval
// as a side-effect it also defeats HTTP (browser) caching
angular.module('myApp').config(function($httpProvider, ...) {
    $httpProvider.interceptors.push(function() {
        return {
            'request': function(config) {
                config.url = getTimeVersionedUrl(config.url);
                return config;
            }
        };
    });

    function getTimeVersionedUrl(url) {
        // only do for html templates of this app
        // NOTE: the path to test for is app dependent!
        if (!url || url.indexOf('a/app/') < 0 || url.indexOf('.html') < 0) return url;
        // create a URL param that changes every minute
        // and add it intelligently to the template's previous url
        var param = 'v=' + ~~(Date.now() / 60000) % 10000; // 4 unique digits every minute
        if (url.indexOf('?') > 0) {
            if (url.indexOf('v=') > 0) return url.replace(/v=[0-9](4)/, param);
            return url + '&' + param;
        }
        return url + '?' + param;
    }

Sono interessato alla tua soluzione - mantieni questo codice per sempre - o per un certo periodo di tempo dopo aver apportato una modifica? Motivo: sarebbe preoccupato per le prestazioni. Inoltre, menzioni le sconfitte degli effetti collaterali HTTP. Intendi dire che è un buon effetto collaterale corretto - nel senso che è quello che intendevi per avere "hotfix"? Grazie
Jamie

1
Lascio questo codice per sempre, anche se abbiamo ripristinato la durata del busting della cache a 10 minuti. Quindi, ogni 10 minuti di utilizzo, un utente ricaricherà nuovi modelli html. Per la mia app biz questo è un costo accettabile per ottenere la possibilità di creare modelli hot-patch, ma ovviamente comprometterebbe troppo le prestazioni per alcuni tipi di app. ... È un peccato che anche la cache HTTP venga sconfitta, ma non vedo un modo per sconfiggere in modo intelligente la cache dei template angolari senza sconfiggere anche l'etag ecc. La memorizzazione nella cache dei modelli angolari IMO non è abbastanza configurabile.
Bradrad2k,

1
+1 Ho avuto la stessa idea, ma intercetto solo per localhost. È possibile vedere la mia realizzazione del intercettore qui: overengineer.net/...
joshcomley

@joshcomley Se hai solo bisogno di sconfiggere la cache su localhost, perché non utilizzare un plug-in del browser che sconfigge tutta la cache?
bradw2k,

@ bradw2k in questo modo ho il controllo completo di ciò che è e non è memorizzato nella cache, il che può essere utile per lo sviluppo. Provo anche in tutti i browser e non tutti i browser hanno estensioni che fanno ciò di cui ho bisogno. Posso anche avere un indicatore sul sito in fase di sviluppo che mi dice se la memorizzazione nella cache è disabilitata o meno, poiché a volte desidero disabilitare e testare i browser solo per un po '.
joshcomley,

8

Se si utilizza il router UI, è possibile utilizzare un decoratore e aggiornare il servizio $ templateFactory e aggiungere un parametro di stringa di query a templateUrl e il browser caricherà sempre il nuovo modello dal server.

function configureTemplateFactory($provide) {
    // Set a suffix outside the decorator function 
    var cacheBust = Date.now().toString();

    function templateFactoryDecorator($delegate) {
        var fromUrl = angular.bind($delegate, $delegate.fromUrl);
        $delegate.fromUrl = function (url, params) {
            if (url !== null && angular.isDefined(url) && angular.isString(url)) {
                url += (url.indexOf("?") === -1 ? "?" : "&");
                url += "v=" + cacheBust;
            }

            return fromUrl(url, params);
        };

        return $delegate;
    }

    $provide.decorator('$templateFactory', ['$delegate', templateFactoryDecorator]);
}

app.config(['$provide', configureTemplateFactory]);

Sono sicuro che puoi ottenere lo stesso risultato decorando il metodo "when" in $ routeProvider.


Un'alternativa molto migliore è usare un plugin come gulp-angular-templatecache per registrare i template angolari js in $ templateCache. Questo dovrebbe essere usato insieme a gulp-rev. Ogni volta che cambia un modello, verrà creato un nuovo file JavaScript con un numero di revisione diverso e la memorizzazione nella cache non sarà mai un problema.
Aman Mahajan,

3

Ho scoperto che il metodo intercettore HTTP funziona abbastanza bene e consente maggiore flessibilità e controllo. Inoltre, è possibile eseguire il busting della cache per ogni versione di produzione utilizzando un hash di rilascio come variabile buster.

Ecco come appare il metodo dev cachebusting Date.

app.factory('cachebustInjector', function(conf) {   
    var cachebustInjector = {
        request: function(config) {    
            // new timestamp will be appended to each new partial .html request to prevent caching in a dev environment               
            var buster = new Date().getTime();

            if (config.url.indexOf('static/angular_templates') > -1) {
                config.url += ['?v=', buster].join('');
            }
            return config;
        }
    };
    return cachebustInjector;
});

app.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('cachebustInjector');
}]);

1

Ecco un'altra opzione in Chrome.

Premi F12per aprire gli strumenti di sviluppo. Quindi Risorse > Memoria cache > Aggiorna cache .

inserisci qui la descrizione dell'immagine

Mi piace questa opzione perché non devo disabilitare la cache come in altre risposte.


In Chrome v. 54.0.2840.99 a novembre 2016 l'ho trovato in una scheda denominata Application.rather anziché Resources.
StackOverflowUser

0

Non esiste una soluzione per impedire la memorizzazione nella cache del browser / proxy in quanto non è possibile avere il controllo su di essa.

L'altro modo per forzare nuovi contenuti ai tuoi utenti per rinominare il file HTML! Esattamente come fa https://www.npmjs.com/package/grunt-filerev per le risorse.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.