AngularJS deaktiviert das teilweise Caching auf der Entwicklungsmaschine

210

Ich habe Probleme beim Zwischenspeichern von Partials in AngularJS.

In meiner HTML-Seite habe ich:

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

wo meine partials geladen sind.

Wenn ich den HTML-Code in meinem Teil ändere, lädt der Browser immer noch alte Daten.

Gibt es eine Problemumgehung?

Mennion
quelle
4
Nur eine kurze Anmerkung: Ich hatte ein Problem damit, das eher mit den Cache-Steuerelement-Headern zusammenhängt, die meine Flask-Anwendung zurückgesendet hat. Ich habe das Problem umgangen app.config.update(SEND_FILE_MAX_AGE_DEFAULT=0), indem ich es zu meinem hinzugefügt habe flask_app.py. (Ich stelle mir ähnliche Dinge für andere Webserver vor).
Gatoatigrado
4
Wenn Sie Chrome verwenden, führen Sie einfach ein Ctrl+Shift+R(dh Hard Reload) durch, und unabhängig davon, welcher Caching-Mechanismus verwendet wird, ignoriert Chrome es und
ruft
4
Strg + Umschalt + R funktioniert in Chrome nicht, aber auf der Registerkarte "Netzwerk" der Entwicklertools funktioniert das Klicken auf "Cache deaktivieren" einwandfrei. Für mich ist dies ein clientseitiges Problem, das nicht wie viele der folgenden Vorschläge mithilfe von Hacks auf dem Server gelöst werden sollte. Es sollte auf dem Client behoben werden, auf dem das "Problem" besteht. Wenn Sie das Problem auf dem Server beheben und vergessen, das Problem zu beheben, kann die Produktion beeinträchtigt werden.
Ameise Kutschera
8
Strg + Umschalt + R umgeht den Cache für normale Anforderungen. Ajax-Anfragen aus Winkel für ng-include| ng-view| templateUrlwerden von dieser Abkürzung nicht behandelt
André Werlang
2
Sie können nicht alle Endbenutzer beim Besuch der Website zu Strg + Umschalt + R auffordern. Wie lautet also die Antwort auf diese Frage für den Fall ohne Entwicklung? "Für mich ist dies ein clientseitiges Problem, das nicht mit Hacks auf dem Server gelöst werden sollte, wie viele der folgenden Vorschläge." - Ich stimme nicht zu, Sie können Clients in einer Webumgebung nicht steuern, also die Lösung für die Produktion muss anwendungsorientiert sein. Aus diesem Grund habe ich akzeptiert: $ rootScope. $ On ('$ viewContentLoaded', function () {$ templateCache.removeAll ();});
Robert Christian

Antworten:

200

Für die Entwicklung können Sie auch den Browser-Cache deaktivieren. Klicken Sie in den Chrome Dev Tools unten rechts auf das Zahnrad und aktivieren Sie die Option

Cache deaktivieren (solange DevTools geöffnet ist)

Update: In Firefox gibt es die gleiche Option in Debugger -> Einstellungen -> Erweiterter Abschnitt (auf Version 33 überprüft)

Update 2: Obwohl diese Option in einigen Berichten in Firefox angezeigt wird, funktioniert sie nicht. Ich schlage vor, Firebug zu verwenden und die Antwort von Hadaytullah zu befolgen.

LukeSolar
quelle
7
Dies sollte die akzeptierte Antwort sein, da keine Codeänderung erforderlich ist und eher der Anforderung des OP entspricht. Natürlich möchten Sie, dass eine Produktions-App Anforderungen zwischenspeichert. Wenn Sie also das tun, was die oben genannten Vorschläge vorgeschlagen haben, kann dies sich als problematisch erweisen, wenn der Code in einer Produktions-App verbleibt.
Aaron Wagner
Irgendwelche Vorschläge für die anderen Browser wie Firefox?
Lereveme
In Firefox: Debugger> Einstellungen (das Zahnrad) gibt es die gleiche Option.
LukeSolar
5
Dies funktioniert in Firefox nicht. Auch wenn der Cache deaktiviert und die Toolbox geöffnet ist, werden die Vorlagen weiterhin zwischengespeichert.
Patrick J Collins
4
Beeinflusst das Caching auch die Produktion? Was passiert, wenn ich neue Webdateien auf den Server schiebe? Was verhindert, dass nachfolgende Anforderungen von Produktionsclients vorveröffentlichte zwischengespeicherte Versionen laden?
Meffect
111

Aufbauend auf der Antwort von @ Valentyn gibt es eine Möglichkeit, den Cache immer automatisch zu leeren, wenn sich der Inhalt der ng-Ansicht ändert:

myApp.run(function($rootScope, $templateCache) {
   $rootScope.$on('$viewContentLoaded', function() {
      $templateCache.removeAll();
   });
});
Mark Rajcok
quelle
@ user252690 Wahrscheinlich muss auch sichergestellt werden, dass die HTML-Vorlage nicht mit Cache-Headern gesendet wurde. Siehe hier und hier für mögliche Korrekturen
Chris Foster
30
Eine kleine Warnung: Das Leeren des $ templateCache kann tatsächlich unbeabsichtigte Folgen haben. Beispielsweise fügt UI Bootstrap während der Initialisierung Standard-Partials direkt zum $ templateCache hinzu und erwartet später, dass sie dort vorhanden sind.
Strille
@ Strille Ich habe versucht, Angular-UI-Bootstrap Modal zu verwenden. Das Popup war nicht sichtbar. weil $ templateCache.removeAll (); Irgendeine Lösung dafür?
Mukun
4
@ Mukun: Es gibt keine einfache Lösung, wie ich es sehe, außer nicht removeAll () zu verwenden, sondern einfach remove () zu verwenden, um die Schlüssel zu löschen, die Sie löschen müssen. Sie benötigen eine Art Buchhaltung, um zu wissen, welche Schlüssel gelöscht werden müssen.
Strille
Gibt es Möglichkeiten, den Cache nur für einen bestimmten UI-Ansichts-Cache zu löschen?
Gayan
37

Wie in den anderen Antworten erwähnt, kann der Cache hier und hier folgendermaßen geleert werden:

$templateCache.removeAll();

Wie von gatoatigrado im Kommentar vorgeschlagen , scheint dies jedoch nur zu funktionieren, wenn die HTML-Vorlage ohne Cache-Header bereitgestellt wurde.

Das funktioniert also bei mir:

Im Winkel:

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

Sie können Cache-Header auf verschiedene Arten hinzufügen, aber hier sind einige Lösungen, die für mich funktionieren.

Wenn Sie verwenden IIS, fügen Sie dies Ihrer web.config hinzu:

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

Wenn Sie Nginx verwenden, können Sie dies zu Ihrer Konfiguration hinzufügen:

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

Bearbeiten

Ich habe gerade festgestellt, dass die Frage devMaschine erwähnt , aber hoffentlich kann dies noch jemandem helfen ...

Chris Foster
quelle
2
Ja, obwohl dies die ursprüngliche Frage nicht direkt beantwortet, hat mir dies tatsächlich geholfen, das Caching-Problem auf einer Live-Website zu lösen.
Andre
31

Wenn Sie über einen Cache sprechen, der zum Zwischenspeichern von Vorlagen verwendet wird, ohne die gesamte Seite neu zu laden, können Sie ihn wie folgt leeren:

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

Und im Markup:

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

Drücken Sie diese Taste, um den Cache zu löschen.

Valentyn Shybanov
quelle
22

Lösung für Firefox (33.1.1) mit Firebug (22.0.6)

  1. Extras> Web-Tools> Firebug> Öffnen Sie Firebug.
  2. Wechseln Sie in den Firebug-Ansichten zur Ansicht "Net".
  3. Ein Dropdown-Menüsymbol wird neben "Net" (Titel der Ansicht) angezeigt.
  4. Wählen Sie "Browser-Cache deaktivieren" aus dem Dropdown-Menü.
Hadaytullah
quelle
Versucht auf Firebug (2.0.11) und Firefox (38.0.1) auf Mac, aber dies hat nicht funktioniert.
Paullb
19

Dieses Snippet hat mir geholfen, das Zwischenspeichern von Vorlagen loszuwerden

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

Die Details des folgenden Snippets finden Sie unter diesem Link: http://oncodesign.io/2014/02/19/safely-prevent-template-caching-in-angularjs/

Code Streich
quelle
nitpick aber wann ist aktuell nie ein objekt? Ich bin mir nicht sicher, ob es jemals nicht da ist, aberif (!current) { return; }
Nate-Wilkins
Dies verhindert Angulars Caching von routenbasierten Vorlagen, jedoch nicht von Teilelementen mit ng-Include.
Bradw2k
16

Ich poste dies nur, um alle Möglichkeiten abzudecken, da keine der anderen Lösungen für mich funktioniert hat (sie haben unter anderem Fehler aufgrund von Abhängigkeiten von Winkel-Bootstrap-Vorlagen verursacht).

Während Sie eine bestimmte Vorlage entwickeln / debuggen, können Sie sicherstellen, dass sie immer aktualisiert wird, indem Sie einen Zeitstempel in den Pfad einfügen, wie folgt:

       $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();
            };
          }
        });

Beachten Sie das Finale ?nd=' + Date.now()in der templateUrlVariablen.

Diosney
quelle
1
Später können Sie ein festlegen .value('DEBUG', true), um diese Leitung zu aktivieren oder nicht.
Diosney
1
Meine Lösung bestand darin, in der Initialisierungsphase meines Hauptmoduls unter .run(function($rootScope) { $rootScope.DEBUG = true; ...und dann innerhalb der Direktive das $ rootScope wie .directive('filter', ['$rootScope', function($rootScope)...und in die zurückgegebene Objekteigenschaft zu injizieren : templateUrl: '/app/components/filter/filter-template.html' + ($rootScope.DEBUG ? '?n=' + Date.now() : ''). Vielleicht könnten Sie Ihren .value-Ansatz ('DEBUG', wahr) ausarbeiten? Upvoted!
JackLeEmmerdeur
Die Verwendung von .value('DEBUG', trueist die gleiche wie bei $rootScope, jedoch ohne Unordnung :) Sie können später DEBUGin den Controller einspeisen und als normaler Dienst abfragen.
Diosney
Könnten Sie vielleicht den Quellcode in Ihrer Antwort erweitern, um das .value(...)Ding einzuschließen, wenn es nicht zu raffiniert ist? Ich nehme an, das Konzept dahinter ist eine kantige Best Practice, die mir unbekannt ist.
JackLeEmmerdeur
1
Diese Lösung ist sehr nützlich, wenn Sie mit Ionic arbeiten. Das spart mir so viel Zeit, dass die Leber wieder nützlich ist. Danke vielmals!
Ajuser
11

Wie andere bereits gesagt haben, kann das vollständige Caching für Entwicklungszwecke problemlos durchgeführt werden, ohne dass der Code geändert werden muss: Verwenden Sie eine Browsereinstellung oder ein Plugin. Außerhalb von dev entfernen Sie die Vorlagen-URL während $ routeChangeStart (oder $ stateChangeStart für UI-Router) aus dem Cache, um das Angular-Vorlagen-Caching von routenbasierten Vorlagen zu verhindern, wie Shayan gezeigt hat. Dies wirkt sich jedoch NICHT auf das Caching von Vorlagen aus, die von ng-include geladen wurden, da diese Vorlagen nicht über den Router geladen werden.

Ich wollte in der Lage sein, jede Vorlage, einschließlich der von ng-include geladenen, in der Produktion zu fixieren und Benutzer den Hotfix schnell in ihrem Browser erhalten zu lassen, ohne die gesamte Seite neu laden zu müssen. Ich bin auch nicht besorgt darüber, das HTTP-Caching für Vorlagen zu verhindern. Die Lösung besteht darin, jede von der App gestellte HTTP-Anforderung abzufangen, diejenigen zu ignorieren, die nicht für die HTML-Vorlagen meiner App bestimmt sind, und dann der URL der Vorlage einen Parameter hinzuzufügen, der sich jede Minute ändert. Beachten Sie, dass die Pfadprüfung spezifisch für den Pfad der Vorlagen Ihrer App ist. Um ein anderes Intervall zu erhalten, ändern Sie die Mathematik für den Parameter oder entfernen Sie% vollständig, um kein Caching zu erhalten.

// 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;
    }
bradw2k
quelle
Ich interessiere mich für Ihre Lösung - behalten Sie diesen Code für immer bei - oder für einen bestimmten Zeitraum, nachdem Sie eine Änderung vorgenommen haben? Grund - wäre besorgt über die Leistung. Sie erwähnen auch Nebenwirkungen, die HTTP besiegen. Du meinst, das ist ein guter Nebeneffekt, der richtig ist - was bedeutet, dass du das beabsichtigt hast, um 'Hotfix' zu haben? Danke
Jamie
1
Ich lasse diesen Code endgültig ein, obwohl wir die Länge des Cache-Bustings auf 10 Minuten zurückgesetzt haben. Alle 10 Minuten der Nutzung lädt ein Benutzer neue HTML-Vorlagen neu. Für meine Geschäftsanwendung ist dies ein akzeptabler Preis, um Hot-Patch-Vorlagen erstellen zu können, aber offensichtlich würde dies die Leistung für einige Arten von Apps zu stark beeinträchtigen. ... Es ist bedauerlich, dass das HTTP-Caching ebenfalls besiegt ist, aber ich sehe keinen Weg, das Angular-Template-Caching intelligent zu besiegen, ohne auch das etag usw. zu besiegen. Das Zwischenspeichern von IMO-Winkelvorlagen ist einfach nicht konfigurierbar genug.
Bradw2k
1
+1 Ich hatte die gleiche Idee, aber ich fange nur für localhost ab. : Sie können meine Umsetzung des Interceptor hier sehen overengineer.net/...
joshcomley
@joshcomley Wenn Sie nur das Caching auf localhost beenden müssen, verwenden Sie ein Browser-Plugin, das das gesamte Caching verhindert.
Bradw2k
@ bradw2k Auf diese Weise habe ich die vollständige Kontrolle darüber, was zwischengespeichert wird und was nicht, was für die Entwicklung nützlich sein kann. Ich teste auch in allen Browsern und nicht alle Browser haben Erweiterungen, die das tun, was ich brauche. Ich kann auch einen Indikator auf der Website in der Entwicklung haben, der mir sagt, ob das Caching deaktiviert ist oder nicht, da ich manchmal nur für eine Weile über Browser hinweg deaktivieren und testen möchte.
Joshcomley
8

Wenn Sie einen UI-Router verwenden, können Sie einen Decorator verwenden und den Dienst $ templateFactory aktualisieren und einen Abfragezeichenfolgenparameter an templateUrl anhängen. Der Browser lädt die neue Vorlage immer vom 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]);

Ich bin sicher, dass Sie das gleiche Ergebnis erzielen können, indem Sie die "when" -Methode in $ routeProvider dekorieren.

Aman Mahajan
quelle
Eine viel bessere Alternative ist die Verwendung eines Plugins wie gulp-angle-templatecache, um eckige js-Vorlagen im $ templateCache zu registrieren. Dies sollte zusammen mit gulp-rev verwendet werden. Jedes Mal, wenn sich eine Vorlage ändert, wird eine neue JavaScript-Datei mit einer anderen Versionsnummer erstellt, und das Zwischenspeichern wird niemals ein Problem sein.
Aman Mahajan
3

Ich fand, dass die HTTP-Interceptor-Methode ziemlich gut funktioniert und zusätzliche Flexibilität und Kontrolle ermöglicht. Darüber hinaus können Sie für jede Produktionsversion einen Cache-Bust durchführen, indem Sie einen Release-Hash als Buster-Variable verwenden.

So sieht die Dev-Cachebusting-Methode aus 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');
}]);
cpreid
quelle
1

Hier ist eine weitere Option in Chrome.

Klicken Sie F12, um Entwicklertools zu öffnen. Dann Ressourcen > Cache-Speicher > Caches aktualisieren .

Geben Sie hier die Bildbeschreibung ein

Ich mag diese Option, weil ich den Cache nicht wie in anderen Antworten deaktivieren muss.

Jess
quelle
In Chrome v. 54.0.2840.99 im November 2016 fand ich dies auf einer Registerkarte namens Application.rather than Resources.
StackOverflowUser
0

Es gibt keine Lösung, um das Zwischenspeichern von Browsern / Proxys zu verhindern, da Sie nicht die Kontrolle darüber haben können.

Die andere Möglichkeit, Ihren Benutzern neuen Inhalt zu erzwingen, besteht darin, die HTML-Datei umzubenennen! Genau wie https://www.npmjs.com/package/grunt-filerev für Assets.

Thomas Decaux
quelle