Frage Wie erzwinge ich den Browser, um zwischengespeicherte CSS / JS-Dateien neu zu laden?


Ich habe bemerkt, dass einige Browser (insbesondere Firefox und Opera) sehr eifrig sind, um zwischengespeicherte Kopien von .css und .js Dateien, sogar zwischen Browsersitzungen. Dies führt zu einem Problem beim Aktualisieren einer dieser Dateien, aber der Browser des Benutzers verwendet die zwischengespeicherte Kopie weiterhin.

Die Frage ist: Was ist der eleganteste Weg, um den Browser des Benutzers zu zwingen, die Datei neu zu laden, wenn sie sich geändert hat?

Im Idealfall würde die Lösung den Browser nicht zwingen, die Datei bei jedem Besuch der Seite neu zu laden. Ich werde meine eigene Lösung als eine Antwort veröffentlichen, aber ich bin neugierig, ob jemand eine bessere Lösung hat und ich werde Ihre Wahl entscheiden lassen.

Aktualisieren:

Nachdem ich eine Weile hier diskutiert habe, habe ich gefunden John Millikin und da5idVorschlag, um nützlich zu sein. Es stellt sich heraus, dass es einen Begriff dafür gibt: automatische Versionierung.

Ich habe unten eine neue Antwort gepostet, die eine Kombination aus meiner ursprünglichen Lösung und Johns Vorschlag ist.

Eine andere Idee, die von vorgeschlagen wurde SCdF wäre, eine falsche Abfragezeichenfolge an die Datei anzuhängen. (Einige Python-Codes zur automatischen Verwendung des Zeitstempels als falsche Abfragestrings wurden von gesendet Pi.). Es gibt jedoch eine Diskussion darüber, ob der Browser eine Datei mit einer Abfragezeichenfolge zwischenspeichern würde. (Denken Sie daran, dass der Browser die Datei zwischenspeichern und bei zukünftigen Besuchen verwenden soll. Wir möchten nur, dass die Datei erneut abgerufen wird, wenn sie sich geändert hat.)

Da nicht klar ist, was mit einer falschen Abfragezeichenfolge passiert, akzeptiere ich diese Antwort nicht.


865
2017-09-23 03:07


Ursprung


Antworten:


Aktualisieren:  Umgeschrieben, um Vorschläge von aufzunehmen John Millikin und da5id. Diese Lösung ist in PHP geschrieben, sollte aber leicht an andere Sprachen angepasst werden.

Update 2: Kommentare von aufnehmen Nick Johnson dass das Original .htaccess Regex kann Probleme mit Dateien wie verursachen json-1.3.js. Lösung ist nur neu zu schreiben, wenn es genau 10 Ziffern am Ende gibt. (Weil 10 Ziffern alle Zeitstempel vom 9/9/2001 bis zum 11/20/2286 abdecken.)

Zuerst verwenden wir die folgende Rewrite-Regel in .htaccess:

RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]

Jetzt schreiben wir die folgende PHP-Funktion:

/**
 *  Given a file, i.e. /css/base.css, replaces it with a string containing the
 *  file's mtime, i.e. /css/base.1221534296.css.
 *  
 *  @param $file  The file to be loaded.  Must be an absolute path (i.e.
 *                starting with slash).
 */
function auto_version($file)
{
  if(strpos($file, '/') !== 0 || !file_exists($_SERVER['DOCUMENT_ROOT'] . $file))
    return $file;

  $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $file);
  return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $file);
}

Wo auch immer Sie Ihr CSS einbinden, ändern Sie es von diesem:

<link rel="stylesheet" href="/css/base.css" type="text/css" />

Zu diesem:

<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />

Auf diese Weise müssen Sie das Link-Tag nicht mehr ändern und der Benutzer wird immer das neueste CSS sehen. Der Browser kann die CSS-Datei zwischenspeichern, aber wenn Sie Änderungen an Ihrem CSS vornehmen, sieht der Browser dies als neue URL, sodass die zwischengespeicherte Kopie nicht verwendet wird.

Dies kann auch mit Bildern, Favicons und JavaScript funktionieren. Grundsätzlich alles, was nicht dynamisch generiert wird.


415
2017-09-23 04:04



Einfache clientseitige Technik

Im Allgemeinen ist Caching gut. Es gibt also ein paar Techniken, je nachdem, ob Sie das Problem selbst beheben, während Sie eine Website entwickeln, oder ob Sie versuchen, den Cache in einer Produktionsumgebung zu steuern.

Allgemeine Besucher Ihrer Website haben nicht die gleiche Erfahrung, die Sie beim Entwickeln der Website haben. Da der durchschnittliche Besucher seltener auf die Seite kommt (vielleicht nur ein paar Mal pro Monat, es sei denn, Sie sind ein Google oder hi5 Networks), ist es weniger wahrscheinlich, dass Ihre Dateien im Cache gespeichert sind, und das könnte ausreichen. Wenn Sie eine neue Version im Browser erzwingen möchten, können Sie der Anforderung immer eine Abfragezeichenfolge hinzufügen und die Versionsnummer erhöhen, wenn Sie größere Änderungen vornehmen:

<script src="/myJavascript.js?version=4"></script>

Dadurch wird sichergestellt, dass jeder die neue Datei erhält. Es funktioniert, weil der Browser die URL der Datei untersucht, um festzustellen, ob eine Kopie im Cache vorhanden ist. Wenn Ihr Server nicht dafür eingerichtet ist, etwas mit der Abfragezeichenfolge zu tun, wird er ignoriert, aber der Name sieht für den Browser wie eine neue Datei aus.

Wenn Sie hingegen eine Website entwickeln, möchten Sie die Versionsnummer nicht jedes Mal ändern, wenn Sie eine Änderung in Ihrer Entwicklungsversion speichern. Das wäre langweilig.

Während Sie Ihre Website entwickeln, wäre es ein guter Trick, automatisch einen Abfragezeichenfolgenparameter zu generieren:

<!-- Development version: -->
<script>document.write('<script src="/myJavascript.js?dev=' + Math.floor(Math.random() * 100) + '"\><\/script>');</script>

Das Hinzufügen einer Abfragezeichenfolge zur Anforderung ist eine gute Möglichkeit, eine Ressource zu versionieren, für eine einfache Website ist dies jedoch möglicherweise nicht erforderlich. Und denken Sie daran, Caching ist eine gute Sache.

Es ist auch erwähnenswert, dass der Browser nicht unbedingt geizig ist, Dateien im Cache zu behalten. Browser haben Richtlinien für diese Art von Dingen, und sie spielen normalerweise nach den Regeln, die in der HTTP-Spezifikation festgelegt sind. Wenn ein Browser eine Anfrage an einen Server sendet, ist ein Teil der Antwort ein EXPIRES-Header. Ein Datum, das dem Browser mitteilt, wie lange es im Cache verbleiben soll. Wenn der Browser das nächste Mal auf eine Anforderung für die gleiche Datei stößt, sieht er, dass er eine Kopie im Cache hat und sucht nach dem EXPIRES-Datum, um zu entscheiden, ob er verwendet werden soll.

Also glauben Sie es oder nicht, es ist tatsächlich Ihr Server, der diesen Browser-Cache so hartnäckig macht. Sie könnten Ihre Servereinstellungen anpassen und die EXPIRES-Header ändern, aber die kleine Technik, die ich oben geschrieben habe, ist wahrscheinlich eine viel einfachere Möglichkeit für Sie, dies zu tun. Da Caching gut ist, sollten Sie dieses Datum normalerweise weit in die Zukunft setzen (ein "Far-future Expires Header") und die oben beschriebene Technik verwenden, um eine Änderung zu erzwingen.

Wenn Sie sich für weitere Informationen über HTTP interessieren oder wie diese Anfragen gestellt werden, ist ein gutes Buch "High Performance Web Sites" von Steve Souders. Es ist eine sehr gute Einführung in das Thema.


162
2017-09-23 13:25



Google's mod_pagespeed Plugin für Apache wird die automatische Versionierung für Sie tun. Es ist wirklich glatt.

Es analysiert HTML auf seinem Weg aus dem Webserver (arbeitet mit PHP, Rails, Python, statischem HTML - irgendetwas) und schreibt Links zu CSS, JS, Bilddateien um, so dass sie einen ID-Code enthalten. Es lädt die Dateien unter den geänderten URLs mit einer sehr langen Cache-Kontrolle auf ihnen. Wenn sich die Dateien ändern, werden die URLs automatisch geändert, sodass der Browser sie erneut abrufen muss. Es funktioniert im Grunde nur, ohne Änderungen an Ihrem Code. Es wird sogar Ihren Code auf dem Weg nach draußen minimieren.


110
2017-09-23 03:12



Anstatt die Version manuell zu ändern, würde ich empfehlen, einen MD5-Hash der eigentlichen CSS-Datei zu verwenden.

Also wäre deine URL so etwas wie

http://mysite.com/css/[md5_hash_here]/style.css

Sie können zwar immer noch die Rewrite-Regel verwenden, um den Hash zu entfernen, aber der Vorteil ist, dass Sie jetzt Ihre Cache-Richtlinie auf "Cache für immer" setzen können, da die Datei unverändert ist, wenn die URL die gleiche ist.

Sie können dann ein einfaches Shell-Skript schreiben, das den Hash der Datei berechnet und Ihr Tag aktualisiert (Sie sollten es wahrscheinlich in eine separate Datei für die Aufnahme verschieben).

Führen Sie dieses Skript einfach jedes Mal aus, wenn sich CSS ändert, und Sie sind gut. Der Browser lädt NUR Ihre Dateien neu, wenn sie geändert werden. Wenn Sie eine Bearbeitung vornehmen und diese dann rückgängig machen, können Sie problemlos herausfinden, in welche Version Sie zurückkehren müssen, damit Ihre Besucher sie nicht erneut herunterladen können.


90
2017-09-23 03:21



Ich bin mir nicht sicher, warum Sie so viele Schmerzen haben, um diese Lösung zu implementieren.

Alles was Sie tun müssen, um den geänderten Zeitstempel der Datei zu erhalten und ihn als Querystring an die Datei anzuhängen

In PHP würde ich es tun als:

<link rel="stylesheet" href="mycss.css?v=<?php echo filemtime('mycss.css') ?>"/>

filemtime ist eine PHP-Funktion, die den modifizierten Zeitstempel der Datei zurückgibt.


57
2017-09-23 06:02



Sie können einfach setzen ?foo=1234 Am Ende deines css / js-Imports änderst du 1234, um zu sein, was auch immer du magst. Sehen Sie sich die SO-HTML-Quelle für ein Beispiel an.

Die Idee ist, dass das? Parameter werden auf der Anforderung trotzdem verworfen / ignoriert und Sie können diese Nummer ändern, wenn Sie eine neue Version bereitstellen.


Hinweis: Es gibt einige Argumente dafür, wie sich dies auf das Caching auswirkt. Ich glaube, der allgemeine Kern davon ist, dass GET Anfragen, mit oder ohne Parameter sollte cachable, also sollte die obige Lösung funktionieren.

Es liegt jedoch sowohl an dem Webserver, zu entscheiden, ob er sich an den Teil der Spezifikation halten möchte, als auch an dem Browser, den der Benutzer verwendet, da er einfach weitermachen und trotzdem nach einer neuen Version fragen kann.


50
2017-08-05 16:55



Ich habe das "Auto Versioning" genannt. Die gebräuchlichste Methode besteht darin, die mtime-Datei der statischen Datei irgendwo in die URL einzufügen und sie mit Rewrite-Handlern oder URL-confs zu entfernen:

Siehe auch:


37
2018-06-24 23:20



Die etwa 30 vorhandenen Antworten sind ein guter Ratschlag für eine circa 2008 Website. Wenn es jedoch um eine moderne, einzelne Seite Anwendung (SPA) könnte es an der Zeit sein, einige grundlegende Annahmen zu überdenken ... insbesondere die Idee, dass es wünschenswert ist, dass der Webserver nur die einzige, neueste Version einer Datei bereitstellt.

Stellen Sie sich vor, Sie sind ein Benutzer mit einer Version M eines in Ihren Browser geladenen SPA:

  1. Ihre CD-Pipeline stellt die neue Version bereit N der Anwendung auf dem Server
  2. Sie navigieren innerhalb des SPA, der ein XHR an den Server sendet /some.template
    • (Ihr Browser hat die Seite nicht aktualisiert, sodass Sie weiterhin die Version ausführen M)
  3. Der Server antwortet mit dem Inhalt von /some.template - Soll die Version zurückgegeben werden? M oder N der Vorlage?

Wenn das Format von /some.template zwischen den Versionen gewechselt M und N (oder die Datei wurde umbenannt oder was auch immer) Wahrscheinlich willst du keine Version N der Vorlage, die an den Browser gesendet wurde, auf dem die alte Version ausgeführt wird M des Parsers. †

In Web-Apps tritt dieses Problem auf, wenn zwei Bedingungen erfüllt sind:

  • Ressourcen werden asynchron nach dem ersten Laden der Seite angefordert
  • Die App-Logik geht von Dingen aus, die sich in zukünftigen Versionen ändern können

Sobald Ihre App mehrere Versionen gleichzeitig bereitstellen muss, Caching und "Nachladen" zu lösen wird trivial:

  1. Installieren Sie alle Websitedateien in versionierten Verzeichnissen: /v<release_tag_1>/…files…, /v<release_tag_2>/…files…
  2. Legen Sie HTTP-Header fest, damit Browser Dateien für immer zwischenspeichern können
    • (Oder noch besser, legen Sie alles in ein CDN)
  3. Alle aktualisieren <script> und <link> Tags, etc., um auf diese Datei in einem der versionierten Verzeichnisse zu zeigen

Dieser letzte Schritt klingt knifflig, da Sie für jede URL in Ihrem serverseitigen oder clientseitigen Code einen URL-Builder aufrufen müssen. Oder du könntest den schlauen Gebrauch machen <base> Etikett und ändern Sie die aktuelle Version an einem Ort.

† Eine Möglichkeit ist es, aggressiv zu sein, wenn der Browser gezwungen wird, alles neu zu laden, wenn eine neue Version veröffentlicht wird. Aber um Operationen, die gerade ausgeführt werden, zu beenden, kann es immer noch am einfachsten sein, mindestens zwei Versionen parallel zu unterstützen: v-current und v-previous.


19
2017-09-23 03:23



Verwenden Sie nicht foo.css? Version = 1! Browser sollen keine URLs mit GET-Variablen zwischenspeichern. Gemäß http://www.thinkvitamin.com/features/webapps/serving-jacascript-fastObwohl IE und Firefox dies ignorieren, tun dies Opera und Safari nicht! Verwenden Sie stattdessen foo.v1234.css und verwenden Sie Umschreibungsregeln, um die Versionsnummer zu entfernen.


14
2017-09-23 13:54



Die RewriteRule benötigt ein kleines Update für js oder css Dateien, die am Ende eine Punktnotation Versionierung enthalten. Z.B. json-1.3.js.

Ich habe der Regex eine Punktnegationsklasse [^.] Hinzugefügt, also .number. wird ignoriert.

RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]

9
2017-09-24 11:38



Für ASP.NET 4.5 und höher können Sie verwenden Skriptbündelung.

Die Anfrage http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81 ist für das Paket AllMyScripts und enthält ein Abfragezeichenfolgepaar v = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81. Die Abfragezeichenfolge v hat ein Wert-Token, das eine eindeutige Kennung zum Zwischenspeichern ist. Solange sich das Paket nicht ändert, fordert die ASP.NET-Anwendung das AllMyScripts-Paket mit diesem Token an. Wenn sich eine Datei im Bundle ändert, generiert das ASP.NET-Optimierungsframework ein neues Token, das garantiert, dass Browseranforderungen für das Bundle das neueste Bundle erhalten.

Es gibt weitere Vorteile beim Bündeln, einschließlich einer gesteigerten Leistung beim erstmaligen Laden von Seiten mit Verkleinerung.


9