Große Leistungsgewinne lassen sich durch das Browser-Caching von CSS erzielen. Sie stellen sicher, dass Ihr Server eingerichtet ist, um Header zu senden, die dem Browser mitteilen, die CSS-Datei für eine bestimmte Zeit aufzubewahren. Dies ist eine bewährte Methode, die viele, wenn nicht die meisten Websites bereits anwenden.
Hand in Hand mit dem Browser-Caching geht das Cache-Busting. Angenommen, der Browser hat die CSS-Datei ein Jahr lang im Cache gespeichert (nicht ungewöhnlich). Dann möchten Sie das CSS ändern. Sie benötigen eine Strategie, um den Cache zu brechen und den Browser zum Herunterladen einer neuen Kopie des CSS zu zwingen.
Hier sind einige Möglichkeiten.
Das CSS muss im Cache sein, damit dies relevant ist…
Nur um sicherzugehen, hier sehen einige gut aussehende Header für eine gecachte CSS-Datei aus

Wir suchen nach dem Cache-Control und Expires Header. Ich bin kein Experte für Serverkonfigurationen. Ich würde mir wahrscheinlich die H5BP-Serverkonfigurationen ansehen. Aber hier sind einige klassische Wege mit Apache/HTAccess, um das zu erreichen
<FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)(\.gz)?$">
Header set Expires "Thu, 15 Apr 2020 20:00:00 GMT"
</FilesMatch>
<IfModule mod_expires.c>
ExpiresActive on
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
</IfModule>
Query-Strings
Die meisten Browser sehen heutzutage eine URL mit einem anderen Query-String als eine andere Datei und laden eine frische Kopie herunter. Die meisten CDNs unterstützen und empfehlen dies sogar.
<link rel="stylesheet" href="style.css?v=3.4.1">
Kleine Änderung vornehmen? Ändern Sie es zu
<link rel="stylesheet" href="style.css?v=3.4.2">
Sie könnten es sich potenziell einfacher machen, indem Sie eine serverseitige Variable festlegen, die Sie an mehreren Stellen verwenden. Eine Änderung würde somit den Cache für viele Dateien gleichzeitig brechen.
<?php $cssVersion = "3.4.2"; ?>
<link rel="stylesheet" href="global.css?v=<?php echo $cssVersion; ?>">
Vielleicht könnten Sie sogar Semantische Versionierung verwenden. Sie könnten auch eine Konstante definieren.
Dateinamen ändern
Query-Strings funktionierten nicht immer. Einige Browser sahen keinen Unterschied bei einem anderen Query-String als eine andere Datei. Und einige Software (habe ich gehört: Squid) würde Dateien mit Query-Strings nicht cachen. Steve Souders sagte uns, dass wir es nicht tun sollen.
Ein ähnliches Konzept war, den Dateinamen selbst zu ändern. Wie hier im HTML
<link rel="stylesheet" href="style.232124.css">
Sie würden dies programmgesteuert behandeln, nicht den Dateinamen in Ihrem Projekt wörtlich ändern. Da diese Datei auf dem Server nicht wirklich existiert, benötigen Sie einige Tricks, um sie zur richtigen Datei zu leiten. Jeremy Keith hat kürzlich seine Technik dafür abgedeckt.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+).(\d+).(js|css)$ $1.$3 [L]
Das weist den Server an, diese Zahlen in JavaScript- und CSS-Dateinamen zu ignorieren, aber der Browser wird sie immer noch als neue Datei interpretieren, wenn ich diese Zahl ändere.
Er verwendet Twig, daher sind die Vorlagen, die er verwendet, letztendlich wie
{% set cssupdate = '20150310' %}
<link rel="stylesheet" href="/css/main.{{ cssupdate }}.css">
Ich bin sicher, Sie können sich eine Version davon in jeder Backend-Sprache vorstellen (wie ASP). Stufe auf, indem Sie ein Build-Tool oder ein Deployment-Skript die Variable selbst aktualisieren lassen.
Cache-Busting „Nummer“ basierend auf dem letzten Aktualisierungsdatum der Datei
Bei der Suche nach diesem Cache-Busting-Zeug sehen Sie viele Ratschläge, die empfehlen, den Server zu verwenden, um zu überprüfen, wann die Datei zuletzt aktualisiert wurde, um die Cache-Busting-„Nummer“ zu erstellen (Nummer bedeutet, was auch immer Sie ändern, um den Cache zu brechen).
function autoversion($url) {
$path = pathinfo($url);
$ver = '.'.filemtime($_SERVER['DOCUMENT_ROOT'].$url).'.';
return $path['dirname'].'/'.str_replace('.', $ver, $path['basename']);
}
<link href="<?php autoversion('/path/to/theme.css'); ?>" rel="stylesheet">
Ich kann dazu nicht gut sprechen. Mir scheint, dass die Aufforderung an Ihren Server, diese Informationen bei jedem Seitenaufruf abzurufen, in der Produktion ziemlich intensiv und gefährlich wäre. Früher habe ich Dinge getan wie „Ich lasse PHP einfach die Dimensionen des Bildes in Datenattributen ausgeben!“, nur um festzustellen, dass der Server zum Stillstand kommt. Seien Sie jedenfalls vorsichtig.
ETags
ETags scheinen eine gute Idee zu sein, denn der Sinn von ihnen ist es, Informationen bereitzustellen, um zu prüfen, ob der Browser bereits eine Kopie dieser Datei hat.
Aber die meisten Ratschläge lauten: „schalten Sie Ihre ETag-Header aus“. Yahoo sagt
Das Problem mit ETags ist, dass sie typischerweise mit Attributen konstruiert werden, die sie für einen bestimmten Server, der eine Website hostet, einzigartig machen. ETags stimmen nicht überein, wenn ein Browser die ursprüngliche Komponente von einem Server abruft und später versucht, diese Komponente auf einem anderen Server zu validieren, eine Situation, die auf Websites, die einen Servercluster zur Bearbeitung von Anfragen verwenden, nur allzu häufig vorkommt.
Ein weiteres Problem ist, dass sie nicht so effektiv sind wie tatsächliches Caching. Um einen ETag zu prüfen, muss immer noch eine Netzwerkanfrage gestellt werden. Es geht nicht nur um den Download von Dateien, der die Leistung beeinträchtigt, sondern um die gesamte Netzwerkaushandlung und Latenz.
Auch hier bin ich kein Experte, aber hier ist, was im Apache-Bereich im Allgemeinen empfohlen wird, um sie auszuschalten
<IfModule mod_headers.c>
Header unset ETag
</IfModule>
FileETag None
Framework macht es für uns
Rails Asset Pipeline
Ich habe einige Erfahrungen mit der Rails Asset Pipeline und Sprockets. Es ist ein Traumsystem, wenn Sie mich fragen. Ich verlinke Stylesheets in Vorlagen
<%= stylesheet_link_tag "about/about" %>
Und es erzeugt HTML wie
<link href="http://assets.codepen.io/assets/about/about-7ca9d3db0013f3ea9ba05b9dcda5ede0.css" media="screen" rel="stylesheet" type="text/css" />
Diese Cache-Busting-Nummer ändert sich nur, wenn sich die Datei ändert, sodass Sie den Cache nur für die Dateien brechen, die gebrochen werden müssen. Außerdem gibt es Methoden für Bilder und JavaScript.
WordPress
Wenn Sie ein Seitencaching-Tool in WordPress verwenden, wie W3 Total Cache oder etwas Ähnliches, müssen Sie wahrscheinlich weniger Angst vor der `filemtime`-Angelegenheit haben, die zu serverintensiv ist.
Gilbert Pellegrom postete eine WordPress-spezifische Technik, die sie verwendet
wp_register_style( 'screen', get_template_directory_uri().'/style.css', array(), filemtime( get_template_directory().'/style.css' ) );
wp_enqueue_style( 'screen' );
// Example Output: /style.css?ver=1384432580
Das WordPress-Plugin Busted! macht im Grunde dasselbe im Hintergrund. Es kümmert sich sozusagen automatisch um alles.
CodeKit
CodeKit hat keine integrierte Methode zum Ändern von Dateinamen, aber es gibt eine Möglichkeit, Shell-Skripte unter von Ihnen festgelegten Umständen auszuführen.

Michael Russell hat einen Blogbeitrag darüber, wie man Zeitstempel in Dateien einfügen kann, den Sie sicher modifizieren könnten, um stattdessen Dateinamen zu ändern.
Build-Tools
Alle beliebten Task-Runner / Build-Tool-Dinger haben Plugins, die beim Ändern von Dateinamen helfen. Sufian Rhazi hat einen Beitrag darüber, wie man es in rohem Node.js macht.
Grunt
Gulp
Brokkoli
Innerhalb von Präprozessoren
Beim Verknüpfen von Assets innerhalb anderer Assets (z. B. eines Bildes, auf das Sie aus einer LESS-Datei verweisen) könnten Sie den Präprozessor zur Arbeit bringen. Ben Nadel hat einen Beitrag, wie man genau das tut.
Async CSS
Da Critical CSS immer wichtiger wird, wird auch das verzögerte Laden von CSS wichtiger. Es gibt auch andere Gründe, CSS verzögert zu laden (vielleicht Druck-CSS oder das Vorbereiten des Caches).
Wenn Sie CSS mit loadCSS laden (oder vielleicht einen Link-Tag einfügen), müssen Sie den angeforderten Dateinamen im JavaScript selbst aktualisieren. Anders als das Ändern des Dateinamens, aber nicht so anders.
Also
Habe ich etwas übersehen? Was ist Ihre Cache-Busting-Strategie?
Ich arbeite an einer großen Universität (mehr als 12000 Studenten) und wir verwenden den Ansatz „zuletzt aktualisiert“ in einer .NET-Umgebung. Wir hatten keine Probleme damit, dass Server „zum Stillstand kommen“. Wir haben die automatisierten Query-Strings für 4 verschiedene Dateien. Es funktioniert wie ein Zauber und integriert sich hervorragend in unseren Team-Workflow. Mit .NET haben wir auch den Luxus, verschiedene Teile von Seiten oder Steuerelemente zu cachen. Ich kann nichts über andere Sprachen sagen, aber für uns funktioniert es großartig.
Django kann auch eine weite Zukunft an Expire-Headern nutzen, indem es ManifestStaticFileStorage konfiguriert. Es funktioniert ähnlich wie Rails. Verlinken Sie Ihre Dateien so
Und die Ausgabe wird so aussehen
Die Rewrite-Regel im Abschnitt „Dateinamen ändern“ ist falsch. Punkte sollten maskiert werden, z. B.
RewriteRule ^(.+)\.(d+)\.(js|css)$ $1.$3 [L], andernfalls würden sie jedes Zeichen, nicht nur Punkte, abgleichen.Ich denke nicht, dass semantische Versionierung für CSS eine sehr praktische Idee ist. Es wäre ziemlich schwierig und völlig sinnlos, zu verfolgen, was CSS ein Patch, was eine Verbesserung und was eine Breaking Change ist, wenn man einfach jeden Browser zwingen kann, die neueste Version herunterzuladen.
Einverstanden!
Ich benutze einen PHP-Include, der sowohl Minifizierung als auch Cache-Busting durchführt
Wenn die Website in Produktion ist, ist das minimierte CSS natürlich nicht mehr schreibbar und das Skript reduziert sich auf
Das htaccess ist dasselbe wie Stephano sagte
Natürlich braucht man je nach CMS nicht all das zu tun :)
Das Überprüfen einer mtime ist eine sehr schnelle Operation, sicherlich im Vergleich dazu, den Server zu bitten, Bilddateien zu öffnen, zu lesen und zu parsen, um ihre Pixelabmessungen zu erhalten.
Persönlich würde ich empfehlen, die Entwicklerzeit zu optimieren und den Server die mtime überprüfen zu lassen. Sie können die Leistung später immer noch optimieren, wenn die Leistung nicht ausreicht (das wird nicht passieren).
So macht es W3 Total Cache. Rails scheint eine MD5 zu berechnen, was eine weitaus intensivere Operation ist (aber immer noch unwahrscheinlich, dass sie einen Webserver belastet).
^ Ich denke, dieser Kommentar ist genau richtig.
Ich wäre überrascht, wenn selbst das Abrufen von Bildgrößen einen Server merklich verlangsamen würde – vorausgesetzt, wir sprechen von lokalen Bildern und nicht von Bildern, die sich auf einer anderen Website/einem anderen Server befinden (die zuerst heruntergeladen werden müssten!).
Dateizugriff ist nicht so schlimm, wie Sie vielleicht denken. Beachten Sie, dass ein typisches Framework (z. B. Rails) für jede Anfrage auf eine **große** Anzahl von Dateien zugreifen muss – das ist nur zum Booten des Frameworks. In PHP können Sie überprüfen, wie viele durch Hinzufügen dieses Codes zu Ihrer Seite
<?php var_dump( count(get_included_files()) ) ?>Ich habe das gerade auf meiner Website überprüft, die Laravel 4 verwendet. Ich habe 200 Dateien, die bei jeder Anfrage geladen werden.
Oh, und weil Zahlen gut sind…
Sehen Sie diesen Kommentar auf der PHP-Seite get_image_size(), wo jemand einige Tests durchgeführt hat. Anscheinend dauerte es 0,15 Sekunden, die Bildabmessungen von 10.000 Dateien abzurufen.
Verdammt, inkompetente Leute wie ich könnten einen Bearbeitungsbutton gebrauchen. ;)
http://php.net/manual/en/function.getimagesize.php#94178
Ich bevorzuge auch die MD5-Methode, da der Dateizugriff vom Dateisystem-Layer gecacht wird und Sie ihn auch in Twig einfügen können. z. B.
gulp-rev ist wahrscheinlich das gängigste Gulp-Plugin dafür.
Es erzeugt einen Dateinamen basierend auf dem Inhalt der Datei sowie eine JSON-Datei mit einer Liste aller geänderten Namen und wozu sie geändert wurden.
Ich bin etwas verwirrt bezüglich der Empfehlung, ETags zu entfernen. Dieser Artikel von Ilya Grigorik empfiehlt, sie hinzuzufügen, aber H5BP scheint sie zu entfernen
Ich nehme an, wenn Sie nur einen Server verwenden, sollten Sie ETags hinzufügen, sonst entfernen?
Der Kommentar auf der H5BP-GitHub-Seite gibt einen Hinweis. ETags sind sinnlos, wenn Sie weit entfernte Gültigkeitsdauern setzen.
Ein ETag kann dem Server mitteilen, ob die Datei seit dem letzten Download geändert wurde. Wenn Sie Gültigkeitsdauern von (sagen wir) einem Jahr oder 10 Jahren festlegen, wird der Browser niemals fragen – denn lange bevor dieses Datum erreicht ist, wird das Element sowieso aus dem Browser-Cache gelöscht und muss neu heruntergeladen werden.
Es ist besser, den Browser gar nicht erst fragen zu lassen, da Sie weniger „Gespräche“ zwischen Browser und Server über HTTP haben.
Natürlich ist der „Nachteil“ (wenn man es so nennen kann) des Festlegens einer sehr langen Gültigkeitsdauer, dass Sie sich mit Cache-Busting befassen müssen.
Toller Artikel wie immer, sehr informativ und hilfreich. Mir ist nur ein kleiner Rechtschreibfehler im zweiten Satz unter „Dateinamen ändern“ aufgefallen, es fehlt ein „e“ bei „different“.
Wir verwenden
gulp-rev. Ich habe es hier nicht erwähnt gesehen, aber es ist ähnlich zu dem, was Rails, Laravel Elixir usw. tun.Grundsätzlich verwenden Sie
gulp-rev, um Ihre Asset-Dateien basierend auf ihrem Inhalt umzubenennen. Beachten Sie, dass dies für CSS-, JS-, Bilddateien und alles andere funktioniert. Sobald Ihre Dateien umbenannt wurden, lesen Sie die Manifestdatei, um den ursprünglichen Dateinamen dem umbenannten Dateinamen zuzuordnen und diesen stattdessen bereitzustellen.Dies ist extrem schnell, da der Server eine reguläre Datei bereitstellt – keine Rewrites, keine PHP-Logik, nichts. Und wenn Sie sich Sorgen machen, das Manifest bei jedem Seitenaufruf zu lesen, können Sie es einfach in Redis oder Memcached cachen, um schnelle Lookups zu ermöglichen.
Der andere Vorteil hierbei ist, dass Sie keinen manuellen Versionszähler mehr pflegen müssen. Alles, was Sie tun müssen, ist, einen Gulp-Task auszuführen, und Sie sind fertig. Und wenn eine bestimmte Datei nicht geändert wird, ändert sich auch ihr Dateiname nicht, sodass der Cache für diese spezielle Datei nicht gebrochen wird.
gulp-rev ist wahrscheinlich das gängigste Gulp-Plugin dafür.
Diese Strategien, die ich für meine Website verwende
Ein paar Anmerkungen.
Squid und Query-Strings
Sie erwähnten, dass dies etwas sei, das Steve Souders entdeckt habe. Es hatte mit der Standardkonfiguration von Squid zu tun. Diese Standardkonfiguration wurde in Squid 2.7 geändert, das vor 7 Jahren veröffentlicht wurde.
Ich habe Steve gefragt, ob er dies immer noch als Problem betrachtet. Antwort: „Nicht sicher. Schwer zu testen. Mir sind keine bekannten Probleme bekannt.“
Während 7 Jahre keine Garantie dafür geben, dass Sie nicht auf Squid-Proxys stoßen, die noch mit dieser Standardkonfiguration laufen, würde ich die Verwendung von Query-Strings für Versionierung in die Kategorie „ziemlich sicher“ einordnen.
ETags
Dies ist ein weiteres Problem, bei dem die Standardkonfiguration ein Problem darstellte, diesmal in Apache. Sie verwendete
FileETag INode MTime Size. Das Hauptproblem war die Verwendung von Inodes, die sicherlich von Server zu Server unterschiedlich waren. Dies wurde in 2.4 behoben, als die Standardeinstellung aufFileETag MTime Sizeumgestellt wurde.Da dies etwas ist, das der Webserver steuert, ist die Behebung in den meisten Fällen einfach.
Sie haben Recht, dass dies nicht anstelle von Caching verwendet werden sollte, es sollte (richtig) neben dem Caching verwendet werden.
Nachdem ich im Laufe der Jahre alle Methoden ausprobiert habe, die Chris hier erwähnt, funktioniert nichts so gut wie sehr lange Cache-Zeiten mit Assets, die mit ihrem Checksummen „fingerprinted“ sind. Dies ist der Ansatz, den Rails/Sprockets und Ember standardmäßig verfolgen.
Es ist am konsistentesten und zwingt nur dann zu einem erneuten Download der Assets, wenn sie sich ändern.
Dies trägt zwar etwas zum „Asset-Churn“ bei, wenn Sie ständig viele Änderungen vornehmen. In den meisten Situationen ist dies ein akzeptabler Kompromiss. HTTP/2 wird dies lösen und wird drastisch verändern, wie wir Assets verpacken und ausliefern.
Ich hatte es satt, Dateinamen in EE-Vorlagen manuell zu aktualisieren, also habe ich ein kleines Plugin erstellt, das das Cache-Busting für mich übernimmt: https://github.com/vfalconi/cache-override
Beispiel
produziert
Ich würde gerne mehr davon sehen