Caching-Stile. Die Grundlagen des Client-Caching in klaren Worten und Beispielen. Last-modified, Etag, Expires, Cache-control: max-age und andere Header. URL-Fingerabdruck

💖 Gefällt es dir? Teilen Sie den Link mit Ihren Freunden

Richtig konfiguriertes Caching bietet enorme Leistungsvorteile, spart Bandbreite und senkt die Serverkosten. Viele Websites implementieren das Caching jedoch schlecht, was zu einer Wettlaufsituation führt, die dazu führt, dass miteinander verbundene Ressourcen nicht mehr synchron sind.

Die überwiegende Mehrheit der Best Practices für das Caching lässt sich einem von zwei Mustern zuordnen:

Muster Nr. 1: unveränderlicher Inhalt und langer Cache mit maximalem Alter. Cache-Kontrolle: max-age=31536000
  • Der Inhalt der URL ändert sich nicht, daher...
  • Der Browser oder das CDN können die Ressource problemlos ein Jahr lang zwischenspeichern
  • Zwischengespeicherte Inhalte, die jünger als das angegebene maximale Alter sind, können ohne Rücksprache mit dem Server verwendet werden

Seite: Hey, ich brauche „/script-v1.js“, „/styles-v1.css“ und „/cats-v1.jpg“ 10:24

Cash: Ich bin leer, wie wäre es mit dir, Server? 10:24

Server: OK, hier sind sie. Übrigens: Bargeld sollte man ein Jahr lang nicht länger nutzen. 10:25

Bargeld: Vielen Dank! 10:25

Seite: Hurra! 10:25

Am nächsten Tag

Seite: Hey, ich brauche „/script-v2 .js“, „/styles-v2 .css“ und „/cats-v1.jpg“ 08:14

Kasse: Es gibt ein Bild mit Katzen, aber nicht den Rest. Server? 08:14

Server: Ganz einfach – hier ist das neue CSS & JS. Noch einmal Bargeld: Ihre Haltbarkeit beträgt nicht mehr als ein Jahr. 08:15

Bargeld: Großartig! 08:15

Seite: Danke! 08:15

Cash: Hmm, ich habe „/script-v1.js“ und „/styles-v1.css“ schon seit einiger Zeit nicht mehr verwendet. Es ist Zeit, sie zu entfernen. 12:32

Mit diesem Muster ändern Sie nie den Inhalt einer bestimmten URL, sondern die URL selbst:

Jede URL hat etwas, das sich zusammen mit dem Inhalt ändert. Dies kann eine Versionsnummer, ein Änderungsdatum oder ein Inhalts-Hash sein (den ich für meinen Blog ausgewählt habe).

Die meisten serverseitigen Frameworks verfügen über Tools, mit denen Sie solche Dinge ganz einfach erledigen können (in Django verwende ich Manifest​Static​Files​Storage); Es gibt auch sehr kleine Bibliotheken in Node.js, die die gleichen Probleme lösen, zum Beispiel gulp-rev.

Dieses Muster eignet sich jedoch nicht für Dinge wie Artikel und Blogbeiträge. Ihre URLs können nicht versioniert werden und ihr Inhalt kann sich ändern. Im Ernst, ich habe oft Grammatik- und Zeichensetzungsfehler und muss den Inhalt schnell aktualisieren können.

Muster Nr. 2: veränderlicher Inhalt, der immer auf dem Server validiert wird. Cache-Kontrolle: kein Cache
  • Der Inhalt der URL ändert sich, das heißt...
  • Ohne Angabe des Servers kann keine lokal zwischengespeicherte Version verwendet werden.

Seite: Hey, ich brauche den Inhalt von „/about/“ und „/sw.js“ 11:32

Cash: Ich kann dir nicht helfen. Server? 11:32

Server: Es gibt einige. Bargeld, behalten Sie es bei sich, aber fragen Sie mich, bevor Sie es verwenden. 11:33

Bargeld: Genau! 11:33

Seite: Danke! 11:33

Am nächsten Tag

Seite: Hey, ich brauche noch einmal den Inhalt von „/about/“ und „/sw.js“ 09:46

Bargeld: Nur eine Minute. Server, sind meine Kopien in Ordnung? Die Kopie von „/about/“ stammt von Montag und „/sw.js“ von gestern. 09:46

Server: „/sw.js“ hat sich nicht geändert... 09:47

Bargeld: Cool. Seite, behalten Sie „/sw.js“ bei. 09:47

Server: …aber ich habe „/about/“ neue Version. Bargeld, behalten Sie es, aber vergessen Sie nicht, mich wie beim letzten Mal zuerst zu fragen. 09:47

Bargeld: Verstanden! 09:47

Seite: Großartig! 09:47

Hinweis: Kein Cache bedeutet nicht „nicht zwischenspeichern“, sondern „überprüfen“ (oder erneut validieren) die zwischengespeicherte Ressource auf dem Server. Und no-store weist den Browser an, überhaupt keinen Cache zu verwenden. Außerdem bedeutet „must-revalidate“ keine obligatorische erneute Validierung, sondern dass die zwischengespeicherte Ressource nur verwendet wird, wenn sie jünger als das angegebene max-age ist, und nur andernfalls wird sie erneut validiert. So hat alles angefangen Schlüsselwörter zum Caching.

In diesem Muster können wir der Antwort ein ETag (Versions-ID Ihrer Wahl) oder einen Last-Modified-Header hinzufügen. Wenn der Client das nächste Mal Inhalte anfordert, wird ein If-None-Match bzw. If-Modified-Since ausgegeben, wodurch der Server sagen kann: „Verwenden Sie, was Sie haben, Ihr Cache ist auf dem neuesten Stand“, d. h. HTTP 304 zurückgeben.

Wenn das Senden von ETag / Last-Modified nicht möglich ist, sendet der Server immer den gesamten Inhalt.

Dieses Muster erfordert immer Netzwerkaufrufe und ist daher nicht so gut wie das erste Muster, das ohne Netzwerkanforderungen auskommt.

Es ist nicht ungewöhnlich, dass wir nicht über die Infrastruktur für das erste Muster verfügen, aber es können auch Probleme mit Netzwerkanfragen in Muster 2 auftreten. Daher wird eine Zwischenoption verwendet: kurze max. Alter und veränderbare Inhalte. Das ist ein schlechter Kompromiss.

Die Verwendung von max-age mit veränderlichem Inhalt ist im Allgemeinen die falsche Wahl

Und leider kommt es häufig vor; Github-Seiten sind ein Beispiel.

Vorstellen:

  • /Artikel/
  • /styles.css
  • /script.js

Mit Server-Header:

Cache-Kontrolle: muss erneut validiert werden, maximales Alter = 600

  • Änderungen des URL-Inhalts
  • Wenn der Browser eine zwischengespeicherte Version hat, die älter als 10 Minuten ist, wird diese ohne Rücksprache mit dem Server verwendet
  • Wenn kein solcher Cache vorhanden ist, wird eine Netzwerkanfrage verwendet, wenn möglich mit If-Modified-Since oder If-None-Match

Seite: Hey, ich brauche „/article/“, „/script.js“ und „/styles.css“ 10:21

Cash: Ich habe nichts wie du, Server? 10:21

Server: Kein Problem, hier sind sie. Aber denken Sie daran, Bargeld: Sie können innerhalb der nächsten 10 Minuten verwendet werden. 10:22

Bargeld: Ja! 10:22

Seite: Danke! 10:22

Seite: Hey, ich brauche wieder „/article/“, „/script.js“ und „/styles.css“ 10:28

Cash: Ups, es tut mir leid, aber ich habe „/styles.css“ verloren, aber ich habe alles andere, los geht's. Server, können Sie „/styles.css“ für mich anpassen? 10:28

Server: Ganz einfach, er hat sich bereits verändert, seit du ihn das letzte Mal mitgenommen hast. Sie können es die nächsten 10 Minuten bedenkenlos verwenden. 10:29

Bargeld: Kein Problem. 10:29

Seite: Danke! Aber es scheint, dass etwas schief gelaufen ist! Alles ist kaputt! Was ist los? 10:29

Dieses Muster hat beim Testen das Recht auf Leben, aber es macht in einem echten Projekt alles kaputt und ist sehr schwer zu verfolgen. Im obigen Beispiel hat der Server HTML, CSS und JS aktualisiert, die Seite wird jedoch mit dem alten zwischengespeicherten HTML und JS sowie dem aktualisierten CSS vom Server gerendert. Versionskonflikte ruinieren alles.

Wenn wir wesentliche Änderungen an HTML vornehmen, ändern wir häufig sowohl das CSS, um die neue Struktur richtig widerzuspiegeln, als auch das JavaScript, um mit dem Inhalt und dem Stil Schritt zu halten. Alle diese Ressourcen sind unabhängig, aber Caching-Header können dies nicht ausdrücken. Infolgedessen können sich Benutzer selbst finden letzte Version eine/zwei Ressourcen und die alte Version des Rests.

max-age wird relativ zur Antwortzeit festgelegt. Wenn also alle Ressourcen als Teil einer einzelnen Adresse übertragen werden, laufen sie gleichzeitig ab, es besteht jedoch immer noch eine geringe Wahrscheinlichkeit einer Desynchronisierung. Wenn Sie Seiten haben, die kein JavaScript oder andere Stile enthalten, sind deren Cache-Ablaufdaten nicht synchron. Und was noch schlimmer ist: Der Browser ruft ständig Inhalte aus dem Cache ab, ohne zu wissen, dass HTML, CSS und JS voneinander abhängig sind, sodass er problemlos eine Sache aus der Liste ziehen und alles andere vergessen kann. Wenn Sie alle diese Faktoren zusammen berücksichtigen, sollten Sie sich darüber im Klaren sein, dass die Wahrscheinlichkeit nicht übereinstimmender Versionen recht hoch ist.

Für den Benutzer kann das Ergebnis ein fehlerhaftes Seitenlayout oder andere Probleme sein. Von kleinen Pannen bis hin zu völlig unbrauchbaren Inhalten.

Glücklicherweise haben Benutzer einen Notausgang...

Manchmal hilft es, die Seite zu aktualisieren

Wenn die Seite durch Aktualisieren geladen wird, führen Browser immer eine serverseitige erneute Validierung durch und ignorieren dabei max-age . Wenn also beim Benutzer aufgrund von max-age etwas kaputt ist, kann eine einfache Seitenaktualisierung alles beheben. Aber nachdem die Löffel gefunden wurden, bleiben natürlich noch Sedimente zurück und die Einstellung zu Ihrer Website wird etwas anders sein.

Ein Servicemitarbeiter kann die Lebensdauer dieser Fehler verlängern

Sie haben beispielsweise einen Servicemitarbeiter wie diesen:

Const-Version = „2“; self.addEventListener("install", event => ( event.waitUntil(caches.open(`static-$(version)`) .then(cache => cache.addAll([ "/styles.css", "/script .js" ]))); )); self.addEventListener("activate", event => ( // ...alte Caches löschen... )); self.addEventListener("fetch", event => ( event.respondWith(caches.match(event.request) .then(response => Response || fetch(event.request))); ));

Dieser Servicemitarbeiter:

  • speichert Skripte und Stile im Cache
  • Verwendet den Cache, wenn eine Übereinstimmung vorliegt, andernfalls greift er auf das Netzwerk zu

Wenn wir das CSS/JS ändern, erhöhen wir auch die Versionsnummer, was ein Update auslöst. Da addAll jedoch zuerst auf den Cache zugreift, kann es aufgrund des maximalen Alters und nicht übereinstimmender CSS- und JS-Versionen zu einem Wettlauf kommen.

Sobald sie zwischengespeichert sind, haben wir bis zum nächsten Service-Worker-Update inkompatibles CSS und JS – sofern wir während des Updates nicht erneut in eine Race-Situation geraten.

Sie können das Caching im Service Worker überspringen:

Self.addEventListener("install", event => ( event.waitUntil(caches.open(`static-$(version)`) .then(cache => cache.addAll([ new Request("/styles.css", ( Cache: „no-cache“ )), new Request(“/script.js“, ( Cache: „no-cache“ )) ]))); ));

Leider werden Optionen für das Caching in Chrome/Opera nicht unterstützt und wurden gerade zum Nightly-Build von Firefox hinzugefügt, aber Sie können es selbst tun:

Self.addEventListener("install", event => ( event.waitUntil(caches.open(`static-$(version)`) .then(cache => Promise.all([ "/styles.css", "/script .js" ].map(url => ( // Cache-Bust mit einer zufälligen Abfragezeichenfolge return fetch(`$(url)?$(Math.random())`).then(response => ( // fehlgeschlagen auf 404, 500 usw. if (!response.ok) throw Error("Not ok"); return Cache.put(url, Response); )) ))))); ));

In diesem Beispiel setze ich den Cache mit einer Zufallszahl zurück, aber Sie können noch einen Schritt weiter gehen und beim Erstellen einen Hash des Inhalts hinzufügen (dies ähnelt der Funktionsweise von sw-precache). Dies ist eine Art Implementierung des ersten Musters mit mit JavaScript, funktioniert aber nur mit dem Service Worker, nicht mit Browsern und CDN.

Servicemitarbeiter und HTTP-Cache arbeiten hervorragend zusammen, lassen Sie sie nicht kämpfen!

Wie Sie sehen, können Sie die Caching-Fehler in Ihrem Service Worker umgehen, aber es ist besser, das Problem an der Wurzel zu packen. Richtige Einstellung Caching erleichtert nicht nur die Arbeit des Servicemitarbeiters, sondern hilft auch Browsern, die Servicemitarbeiter nicht unterstützen (Safari, IE/Edge), und ermöglicht Ihnen außerdem, das Beste aus Ihrem CDN herauszuholen.

Richtige Caching-Header können auch die Aktualisierung eines Service Workers erheblich erleichtern.

Const-Version = „23“; self.addEventListener("install", event => ( event.waitUntil(caches.open(`static-$(version)`) .then(cache => cache.addAll([ "/", "/script-f93bca2c. js", "/styles-a837cb1e.css", "/cats-0e9a2ef4.jpg" ]))); ));

Hier habe ich die Stammseite mit Muster Nr. 2 (serverseitige erneute Validierung) und alle anderen Ressourcen mit Muster Nr. 1 (unveränderlicher Inhalt) zwischengespeichert. Jedes Service-Worker-Update führt zu einer Anfrage an die Stammseite und alle anderen Ressourcen werden nur geladen, wenn sich ihre URL geändert hat. Das ist gut, denn es spart Datenverkehr und verbessert die Leistung, unabhängig davon, ob Sie ein Upgrade von einem vorherigen oder einem sehr alten durchführen alte Version.

Hier besteht ein erheblicher Vorteil gegenüber der nativen Implementierung, bei der selbst bei einer kleinen Änderung die gesamte Binärdatei heruntergeladen wird oder ein komplexer Binärvergleich erforderlich ist. Auf diese Weise können wir eine große Webanwendung mit relativ geringer Last aktualisieren.

Servicemitarbeiter arbeiten besser als Verstärkung und nicht als vorübergehende Krücke. Arbeiten Sie also mit dem Cache, anstatt ihn zu bekämpfen.

Bei sorgfältiger Anwendung können maximales Alter und variabler Inhalt sehr gut sein

max-age ist sehr oft die falsche Wahl für veränderbare Inhalte, aber nicht immer. Beispielsweise hat der Originalartikel ein maximales Alter von drei Minuten. Die Race-Bedingung stellt kein Problem dar, da es keine Abhängigkeiten auf der Seite gibt, die dasselbe Caching-Muster verwenden (CSS, JS und Bilder verwenden Muster Nr. 1 – unveränderlicher Inhalt), alles andere verwendet dieses Muster nicht.

Dieses Muster bedeutet, dass ich problemlos einen beliebten Artikel schreiben kann und mein CDN (Cloudflare) den Server entlasten kann, solange ich bereit bin, drei Minuten zu warten, bis der aktualisierte Artikel für Benutzer verfügbar ist.

Dieses Muster sollte ohne Fanatismus verwendet werden. Wenn ich einem Artikel einen neuen Abschnitt hinzugefügt und von einem anderen Artikel aus darauf verlinkt habe, habe ich eine Abhängigkeit erstellt, die aufgelöst werden muss. Der Benutzer kann auf den Link klicken und erhält eine Kopie des Artikels ohne den gewünschten Abschnitt. Wenn ich das vermeiden möchte, sollte ich den Artikel aktualisieren, die zwischengespeicherte Version des Artikels aus Cloudflare löschen, drei Minuten warten und erst dann den Link zu einem anderen Artikel hinzufügen. Ja, dieses Muster erfordert Vorsicht.

Bei richtiger Anwendung sorgt Caching für erhebliche Leistungsverbesserungen und Bandbreiteneinsparungen. Stellen Sie unveränderliche Inhalte bereit, wenn Sie die URL einfach ändern können, oder verwenden Sie eine serverseitige erneute Validierung. Mischen Sie Inhalte mit maximalem Alter und veränderlichen Inhalten, wenn Sie mutig genug und sicher sind, dass Ihre Inhalte keine Abhängigkeiten aufweisen, die aus der Synchronisierung geraten könnten.

Bei Änderungen an Websites kommt es häufig vor, dass der Inhalt von Seiten, CSS-Dateien und Skripten (.js) vom Browser zwischengespeichert wird und über längere Zeit unverändert bleibt. Dies führt dazu, dass Clients an komplexe Kombinationen von F5 oder Strg + F5 gewöhnt werden müssen, damit die vorgenommenen Änderungen in allen Browsern wirksam werden. Und stellen Sie von Zeit zu Zeit sicher, dass sie gedrückt werden.

Der Vorgang ist ziemlich langwierig und umständlich. Sie können der Situation natürlich entkommen, indem Sie die Dateien jedes Mal umbenennen, aber auch das ist umständlich.

Es gibt jedoch eine Möglichkeit, die gleichen Namen beizubehalten und das Caching von .css- oder .js-Dateien in dem Moment zurückzusetzen, in dem wir es brauchen. Und vergessen Sie Strg + F5 für immer.

Unterm Strich hängen wir am Ende einen Pseudoparameter an unsere .css- oder .js-Dateien an, den wir von Zeit zu Zeit ändern und so den Cache im Browser zurücksetzen.

Somit ist der Eintrag in Quellcode wird nun so aussehen:

Wobei 186485 eine willkürliche Kombination ist, die dieselbe Datei ausgibt, der Browser sie jedoch dank des Pseudoparameters als neu interpretiert ?186485

Um nun nicht jedes Mal alle Vorkommen unseres Parameters zu ändern, legen wir ihn in einer PHP-Datei fest, die wir mit allen benötigten Stellen verbinden:



Freunden erzählen