Grunderna i klientcache i tydliga ord och exempel. Senast ändrad, Etag, Expires, Cache-kontroll: max-age och andra rubriker. Bästa metoder för cachelagring Enkel ETag-cachelagring

Genom att inkludera extern CSS och Javascript vill vi minska onödiga HTTP-förfrågningar till ett minimum.

För detta ändamål serveras .js- och .css-filer med rubriker som säkerställer tillförlitlig cachelagring.

Men vad gör du när en av dessa filer ändras under utvecklingen? Alla användare har det i cache gammal version- tills cachen är föråldrad kommer det att finnas många klagomål på trasig integration av servern och klientdelarna.

Korrekt cachelagring och versionshantering eliminerar detta problem helt och ger tillförlitlig, transparent synkronisering av stil-/skriptversioner.

Enkel ETag-cache

Det enklaste sättet att cachelagra statiska resurser är att använda ETag.

Det räcker med att aktivera lämplig serverinställning (för Apache är den aktiverad som standard) - och för varje fil i rubrikerna kommer en ETag att ges - en hash som beror på uppdateringstid, filstorlek och (på inodbaserad filsystem) inode.

Webbläsaren cachar en sådan fil och specificerar vid efterföljande förfrågningar en If-None-Match-rubrik med ETag för det cachade dokumentet. Efter att ha fått en sådan rubrik kan servern svara med kod 304 - och då tas dokumentet från cachen.

Det ser ut så här:

Första begäran till servern (cache clean) GET /misc/pack.js HTTP/1.1 Host: webbplats

I allmänhet lägger webbläsaren vanligtvis till ett gäng rubriker som User-Agent, Acceptera, etc. De är klippta för korthetens skull.

Serversvar Servern svarar med ett dokument med kod 200 och ETag: HTTP/1.x 200 OK Content-Encoding: gzip Content-Type: text/javascript; charset=utf-8 Etag: "3272221997" Acceptera-intervall: bytes Innehållslängd: 23321 Datum: Fre, 02 maj 2008 17:22:46 GMT Server: lighttpd Nästa webbläsarförfrågan Vid nästa begäran lägger webbläsaren till If-None -Match: (cachelagrad ETag): GET /misc/pack.js HTTP/1.1 Host: site If-None-Match: "453700005" Serversvar Servern ser ut - ja, dokumentet har inte ändrats. Det betyder att du kan utfärda en 304-kod och inte skicka dokumentet igen. HTTP/1.x 304 Ej ändrad innehållskodning: gzip Etag: "453700005" Innehållstyp: text/javascript; charset=utf-8 Acceptera-intervall: bytes Datum: Tis, 15 Apr 2008 10:17:11 GMT

Alternativt alternativ- om dokumentet har ändrats skickar servern helt enkelt 200 med den nya ETag.

Kombinationen Last-Modified + If-Modified-Since fungerar på liknande sätt:

  • servern skickar datumet för den senaste ändringen i rubriken Last-Modified (istället för ETag)
  • webbläsaren cachar dokumentet, och nästa gång en begäran om samma dokument görs, skickar den datumet för den cachade versionen i rubriken If-Modified-Since (istället för If-None-Match)
  • servern kontrollerar datumen och om dokumentet inte har ändrats skickar den bara 304-koden, utan innehållet.
  • Dessa metoder fungerar tillförlitligt och bra, men webbläsaren måste fortfarande göra en begäran för varje skript eller stil.

    Smart cachning. Versionering

    Den allmänna metoden för versionering - i ett nötskal:

  • Versionen (eller ändringsdatumet) läggs till i alla skript. Till exempel kommer http://site/my.js att bli http://site/my.v1.2.js
  • Alla skript är hårdcachade av webbläsaren
  • När du uppdaterar skriptet ändras versionen till en ny: http://site/my.v2.0.js
  • Adressen har ändrats, så webbläsaren kommer att begära och cachelagra filen igen
  • Den gamla versionen 1.2 kommer gradvis att falla ur cachen
  • Hård cachning

    Hård cachning- en sorts slägga som helt spikar förfrågningar till servern om cachade dokument.

    För att göra detta, lägg bara till Expires och Cache-Control: max-age rubrikerna.

    Till exempel, för att cachelagra i 365 dagar i PHP:

    Header("Expires: ".gmdate("D, d M Y H:i:s", time()+86400*365)." GMT"); header("Cache-kontroll: max-age="+86400*365);

    Eller så kan du cachelagra innehållet permanent med mod_header i Apache:

    Efter att ha fått sådana rubriker, cachelagrar webbläsaren dokumentet under lång tid. All ytterligare åtkomst till dokumentet kommer att serveras direkt från webbläsarens cache, utan att kontakta servern.

    De flesta webbläsare (Opera, Internet Explorer 6+, Safari) Cache INTE dokument om det finns ett frågetecken i adressen, eftersom de anses vara dynamiska.

    Det är därför vi lägger till versionen i filnamnet. Naturligtvis, med sådana adresser måste du använda en lösning som mod_rewrite, vi kommer att titta på detta senare i artikeln.

    P.S. Men Firefox cachar adresser med frågetecken...

    Automatisk namnupplösning

    Låt oss titta på hur man automatiskt och transparent ändrar versioner utan att byta namn på själva filerna.

    Namn med version -> Fil

    Det enklaste är att göra om namnet med versionen till det ursprungliga filnamnet.

    På Apache-nivå kan detta göras med mod_rewrite:

    RewriteEngine på RewriteRule ^/(.*\.)v+\.(css|js|gif|png|jpg)$ /$1$2 [L]

    Denna regel bearbetar alla css/js/gif/png/jpg-filer och tar bort versionen från namnet.

    Till exempel:

    /images/logo.v2.gif -> /images/logo.gif
    /css/style.v1.27.css -> /css/style.css
    /javascript/script.v6.js -> /javascript/script.js

    Men förutom att klippa ut versionen behöver du också lägga till hårda cachningsrubriker till filerna. Mod_header-direktiven används för detta:

    Rubrik lägg till "Upphör" "Mon, 28 Jul 2014 23:30:00 GMT" Rubrik lägg till "Cache-Control" "max-age=315360000"

    Och tillsammans implementerar den följande Apache-konfiguration:

    RewriteEngine on # tar bort versionen och ställer samtidigt in variabeln att filen är versionerad RewriteRule ^/(.*\.)v+\.(css|js|gif|png|jpg)$ /$1$2 # hard cache versionerade filer Header add "Expires" "Mon, 28 Jul 2014 23:30:00 GMT" env=VERSIONED_FILE Header add "Cache-Control" "max-age=315360000" env=VERSIONED_FILE

    På grund av hur mod_rewrite-modulen fungerar måste RewriteRule placeras i huvudet konfigurationsfil httpd.conf eller inkluderade filer, men aldrig i .htaccess, annars körs Header-kommandona först, innan variabeln VERSIONED_FILE ställs in.

    Rubrikdirektiv kan finnas var som helst, även i .htaccess - det spelar ingen roll.

    Lägger automatiskt till version till filnamnet på HTML-sidan

    Hur du lägger in versionen i skriptets namn beror på ditt mallsystem och i allmänhet hur du lägger till skript (stilar, etc.).

    Till exempel, när du använder ändringsdatumet som en version och Smarty mallmotor, kan länkar ställas in så här:

    Versionsfunktionen lägger till versionen:

    Funktion smarty_version($args)( $stat = stat($GLOBALS["config"]["site_root"].$args["src"]); $version = $stat["mtime"]; echo preg_replace("! \.(+?)$!", ".v$version.\$1", $args["src"]); )

    Resultat på sidan:

    Optimering

    För att undvika onödiga statanrop kan du lagra en array med en lista över aktuella versioner i en separat variabel

    $versions["css"] = array("group.css" => "1.1", "other.css" => "3.0", )

    I det här fallet ersätts den aktuella versionen från arrayen helt enkelt i HTML-koden.

    Du kan korsa båda tillvägagångssätten och under utveckling producera en version efter ändringsdatum - för relevans, och i produktion - en version från en array, för prestanda.

    Tillämplighet

    Denna cachningsmetod fungerar överallt, inklusive Javascript, CSS, bilder, Flash-filmer, etc.

    Det är användbart när dokumentet ändras, men webbläsaren ska alltid ha den aktuella, uppdaterade versionen.

    Cachen spelar en viktig roll i driften av nästan alla webbapplikationer på nivån att arbeta med databaser, webbservrar och även på klienten.

    I den här artikeln kommer vi att försöka förstå klientcachelagring. I synnerhet kommer vi att titta på vilka HTTP-rubriker som används av webbläsare och webbservrar och vad de betyder.

    Men först, låt oss ta reda på varför cachning på klientsidan överhuvudtaget behövs? .

    Webbsidor består av många olika element: bilder, css- och js-filer, etc. Vissa av dessa element används på flera (många) sidor på webbplatsen. Cachning på klientsidan hänvisar till webbläsares förmåga att spara kopior av filer (serversvar) för att inte ladda ner dem igen. Detta gör att du avsevärt kan påskynda omladdningen av sidor, spara på trafik och även minska belastningen på servern.

    Det finns flera olika HTTP-rubriker för att styra cachingprocesser på klientsidan. Låt oss prata om var och en av dem.

    Http-rubriker för att styra klientcachelagring

    Låt oss först titta på hur servern och webbläsaren interagerar i avsaknad av cachning. För en tydlig förståelse försökte jag föreställa mig och visualisera kommunikationsprocessen mellan dem i form av en textchatt. Föreställ dig i några minuter att servern och webbläsaren är personer som korresponderar med varandra :)

    Utan cache (i avsaknad av cachelagrade http-rubriker)

    Som vi kan se kommer webbläsaren att ladda ner den igen från servern varje gång cat.png-bilden visas. Jag tror att det inte finns något behov av att förklara att detta är långsamt och ineffektivt.

    Senaste modifierade svarsrubrik och if-Modified-Since-förfrågan.

    Tanken är att servern lägger till en senast modifierad rubrik till filen (svaret) den ger till webbläsaren.

    Webbläsaren vet nu att filen skapades (eller modifierades) den 1 december 2014. Nästa gång webbläsaren behöver samma fil kommer den att skicka en begäran med rubriken if-Modified-Since.

    Om filen inte har ändrats skickar servern ett tomt svar till webbläsaren med statusen 304 (Ej ändrad). I det här fallet vet webbläsaren att filen inte har uppdaterats och kan visa kopian som den sparade förra gången.

    Med Last-modified sparar vi alltså laddning stor fil, kommer iväg med ett tomt snabbt svar från servern.

    Etag-svarsrubrik och If-None-Match-begäransrubrik.

    Funktionsprincipen för Etag är mycket lik Last-modified, men till skillnad från den är den inte bunden till tid. Tid är en relativ sak.

    Tanken är att när den skapas och varje gång den ändras, taggar servern filen med en speciell tagg som heter ETag, och lägger även till en rubrik till filen (svaret) som den skickar till webbläsaren:

    ETag: "686897696a7c876b7e"

    Nu vet webbläsaren att den aktuella versionsfilen har en ETag lika med "686897696a7c876b7e". Nästa gång webbläsaren behöver samma fil kommer den att skicka en begäran med rubriken If-None-Match: "686897696a7c876b7e" .

    If-None-Match: "686897696a7c876b7e"

    Servern kan jämföra etiketterna och, om filen inte har ändrats, skicka ett tomt svar till webbläsaren med statusen 304 (Ej modifierad). Som med Senast ändrad kommer webbläsaren att ta reda på att filen inte har uppdaterats och kommer att kunna visa en kopia från cachen.

    Titel har löpt ut

    Funktionsprincipen för denna rubrik skiljer sig från Etag och Last-modified som beskrivs ovan. Med Expired bestäms "utgångsdatum" ("relevansperiod") för filen. De där. Vid första laddningen låter servern webbläsaren veta att den inte planerar att ändra filen förrän det datum som anges i Expired:

    Nästa gång kommer webbläsaren, som vet att "utgångsdatumet" ännu inte har kommit, inte ens försöka göra en förfrågan till servern och kommer att visa filen från cachen.

    Denna typ av cache är särskilt relevant för illustrationer för artiklar, ikoner, favoritikoner, vissa css- och js-filer, etc.

    Cache-kontrollhuvud med max-age direktiv.

    Funktionsprincipen för Cache-kontroll: max-age är mycket lik Expired. Här bestäms också filens "utgångsdatum", men det ställs in i sekunder och är inte bundet till en specifik tid, vilket är mycket bekvämare i de flesta fall.

    Som referens:

    • 1 dag = 86400 sekunder
    • 1 vecka = 604800 sekunder
    • 1 månad = 2629000 sekunder
    • 1 år = 31536000 sekunder

    T.ex:

    Cache-kontroll: max-age=2629000;

    Cache-kontrollhuvudet har andra direktiv förutom maxålder. Låt oss ta en snabb titt på de mest populära:

    offentlig
    Faktum är att förfrågningar kan cachelagras inte bara av användarens slutklient (webbläsare), utan också av olika mellanliggande proxyservrar, CDN-nätverk, etc. Så det offentliga direktivet tillåter absolut vilken proxyserver som helst att utföra cachning precis som en webbläsare.

    privat
    Direktivet säger att den här filen(serversvar) är slutanvändarspecifik och bör inte cachelagras av olika mellanliggande proxyservrar. Samtidigt tillåter det cachning till slutklienten (användarens webbläsare). Detta är till exempel relevant för interna användarprofilsidor, förfrågningar inom en session, etc.

    Låter dig ange att klienten ska göra en begäran till servern varje gång. Används ibland med Etag-huvudet som beskrivs ovan.

    ingen butik
    Instruerar klienten att inte under några omständigheter behålla en kopia av begäran eller delar av begäran. Detta är den strängaste rubriken som åsidosätter alla cacher. Den uppfanns speciellt för att arbeta med konfidentiell information.

    måste förnyas
    Detta direktiv instruerar webbläsaren att göra en obligatorisk begäran till servern för att omvalidera innehållet (till exempel om du använder eTag). Faktum är att http i en viss konfiguration tillåter cachen att lagra innehåll som redan är inaktuellt. must-revalidate ålägger webbläsaren att kontrollera innehållets färskhet under alla omständigheter genom att göra en begäran till servern.

    proxy-revalidate
    Detta är detsamma som måste-revalidate, men gäller endast för cachning av proxyservrar.

    s-maxage
    Praktiskt taget inte annorlunda än max-age , förutom att detta direktiv endast beaktas av cachen för olika proxyservrar, men inte av användarens webbläsare själv. Bokstaven "s -" kommer från ordet "delad" (t.ex. CDN). Detta direktiv är specifikt avsett för CDN:er och andra mellanliggande cacher. Att specificera det åsidosätter värdena för direktivet om maxålder och rubriken Expired. Men om du inte bygger CDN-nätverk är det osannolikt att du någonsin kommer att behöva s-maxage.

    Hur kan jag se vilka rubriker som används på en webbplats?

    Du kan se http-förfrågningsrubriker och svarsrubriker i felsökaren i din favoritwebbläsare. Här är ett exempel på hur det ser ut i Chrome:

    Samma sak kan ses i vilken webbläsare eller http-sniffer som helst med respekt för sig själv.

    Ställa in cachning i Apache och Nginx

    Vi kommer inte att återberätta dokumentationen för att konfigurera populära servrar. Du kan alltid se den och. Nedan kommer vi att ge några verkliga exempel för att visa hur konfigurationsfiler ser ut.

    Exempel Apache-konfigurationer för att kontrollera Expires

    Vi sätter olika "utgångsdatum" för olika typer filer. Ett år för bilder, en månad för skript, stilar, pdf-filer och ikoner. För allt annat – 2 dagar.

    ExpiresActive On ExpiresByType image/jpg "access plus 1 year" ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/gif "access plus 1 year" ExpiresByType image/png "access plus 1 year" ExpiresByType text/css "access plus 1 year" month" ExpiresByType application/pdf "access plus 1 month" ExpiresByType text/x-javascript "access plus 1 month" ExpiresByType image/x-icon "access plus 1 year" ExpiresDefault "access plus 2 days"

    Exempel på Nginx-konfiguration för att styra Expires

    Vi anger olika "utgångsdatum" för olika typer av filer. En vecka för bilder, en dag för stilar och manus.

    Server ( #... plats ~* \.(gif|ico|jpe?g|png)(\?+)?$ ( går ut 1w; ) plats ~* \.(css|js)$ ( går ut 1d; ) #...)

    Apache-konfigurationsexempel för cache-kontroll (max-age och public/private/no-cache) Header set Cache-Control "max-age=2592000, public" Header set Cache-Control "max-age=88000, private, must- revalidate" Header set Cache-Control "private, no-store, no-cache, must-revalidate, no-transform, max-age=0" Header set Pragma "no-cache" Nginx-konfigurationsexempel för Cache-kontroll statiska filserver ( #... plats ~* \.(?:ico|css|js|gif|jpe?g|png)$ ( add_header Cache-Control "max-age=88000, public"; ) #... ) I slutsats

    "Cachela allt som kan cachas" är ett bra motto för en webbutvecklare. Ibland kan du lägga bara några timmar på konfiguration och samtidigt förbättra användarupplevelsen av din webbplats avsevärt, minska serverbelastningen avsevärt och spara trafik. Det viktigaste är att inte överdriva det och ställa in allt korrekt, med hänsyn till egenskaperna hos din resurs.

    Korrekt konfigurerad cachning ger enorma prestandafördelar, sparar bandbredd och minskar serverkostnaderna, men många webbplatser implementerar cachning dåligt, vilket skapar ett race-tillstånd som gör att sammankopplade resurser blir osynkroniserade.

    Överväldigande majoritet bästa praxis cachning hänvisar till ett av två mönster:

    Mönster nr 1: oföränderligt innehåll och lång max-age cache Cache-Control: max-age=31536000
    • Innehållet i webbadressen ändras inte, därför...
    • Webbläsaren eller CDN kan enkelt cachelagra resursen i ett år
    • Cachat innehåll som är yngre än den angivna maxåldern kan användas utan att konsultera servern

    Sida: Hej, jag behöver "/script-v1.js", "/styles-v1.css" och "/cats-v1.jpg" 10:24

    Kontanter: Jag är tom, vad sägs om dig, server? 10:24

    Server: OK, här är de. Förresten, Cash, de ska användas i ett år, inte mer. 10:25

    Cash: Tack! 10:25

    Sida: Hurra! 10:25

    Nästa dag

    Sida: Hej, jag behöver "/script-v2 .js", "/styles-v2 .css" och "/cats-v1.jpg" 08:14

    Kontanter: Det finns en bild med katter, men inte resten. Server? 08:14

    Server: Enkelt – här är den nya CSS & JS. Återigen, Cash: deras hållbarhet är inte mer än ett år. 08:15

    Kontanter: Bra! 08:15

    Sida: Tack! 08:15

    Kontanter: Hmm, jag har inte använt "/script-v1.js" & "/styles-v1.css" på ganska länge. Det är dags att ta bort dem. 12:32

    Med det här mönstret ändrar du aldrig innehållet i en specifik webbadress, du ändrar själva webbadressen:

    Varje URL har något som förändras tillsammans med innehållet. Detta kan vara ett versionsnummer, ett ändrat datum eller en hash för innehåll (vilket är vad jag valde för min blogg).

    De flesta ramverk på serversidan har verktyg som låter dig göra saker som detta med lätthet (i Django använder jag Manifest​Static​Files​Storage); Det finns också väldigt små bibliotek i Node.js som löser samma problem, till exempel gulp-rev.

    Det här mönstret är dock inte lämpligt för saker som artiklar och blogginlägg. Deras webbadresser kan inte ändras och deras innehåll kan ändras. Allvarligt talat, jag har ofta grammatiska fel och skiljetecken och måste snabbt kunna uppdatera innehållet.

    Mönster #2: föränderligt innehåll som alltid valideras på servern Cache-Control: no-cache
    • Innehållet i webbadressen kommer att ändras, vilket betyder...
    • Alla lokalt cachade versioner kan inte användas utan att ange servern.

    Sida: Hej, jag behöver innehållet i "/about/" och "/sw.js" 11:32

    Cash: Jag kan inte hjälpa dig. Server? 11:32

    Server: Det finns några. Kontanter, ha dem hos dig, men fråga mig innan du använder dem. 11:33

    Kontanter: Precis! 11:33

    Sida: Tack! 11:33

    Nästa dag

    Sida: Hej, jag behöver innehållet i "/about/" och "/sw.js" igen 09:46

    Kontanter: Bara en minut. Server, är mina kopior okej? Kopian av "/about/" är från måndag och "/sw.js" är från igår. 09:46

    Server: "/sw.js" har inte ändrats... 09:47

    Kontanter: Coolt. sida, behåll "/sw.js" . 09:47

    Server: …men jag har "/about/" ny version. Kontanter, håll det, men som förra gången, glöm inte att fråga mig först. 09:47

    Cash: Jag förstår! 09:47

    Sida: Bra! 09:47

    Obs: no-cache betyder inte "cache inte", det betyder "kontrollera" (eller omvalidera) den cachade resursen på servern. Och no-store säger åt webbläsaren att inte cachelagra alls. Dessutom betyder måste-revalidate inte obligatorisk förlängning, utan att den cachade resursen endast används om den är yngre än den angivna max-åldern och endast i övrigt omvalideras den. Så här började det hela nyckelord för cachelagring.

    I det här mönstret kan vi lägga till en ETag (versions-ID som du väljer) eller en senast ändrad rubrik till svaret. Nästa gång klienten begär innehåll, matas en If-None-Match eller If-Modified-Since ut, vilket gör att servern kan säga "Använd vad du har, din cache är uppdaterad", det vill säga returnera HTTP 304.

    Om det inte går att skicka ETag / Last-Modified skickar servern alltid hela innehållet.

    Det här mönstret kräver alltid nätverksanrop, så det är inte lika bra som det första mönstret, som klarar sig utan nätverksförfrågningar.

    Det är inte ovanligt att vi inte har infrastrukturen för det första mönstret, men det kan också uppstå problem med nätverksförfrågningar i mönster 2. Som ett resultat används ett mellanalternativ: kort maxålder och föränderligt innehåll. Detta är en dålig kompromiss.

    Att använda max-age med föränderligt innehåll är i allmänhet fel val

    Och tyvärr är det vanligt; Github-sidor är ett exempel.

    Tänka:

    • /artikel/
    • /styles.css
    • /script.js

    Med serverhuvud:

    Cache-kontroll: måste omvalideras, max-age=600

    • URL-innehåll ändras
    • Om webbläsaren har en cachad version som är nyare än 10 minuter används den utan att konsultera servern
    • Om det inte finns någon sådan cache används en nätverksbegäran, om möjligt med If-Modified-Since eller If-None-Match

    Sida: Hej, jag behöver "/article/", "/script.js" och "/styles.css" 10:21

    Cash: Jag har ingenting, som du, Server? 10:21

    Server: Inga problem, här är de. Men kom ihåg, kontanter: de kan användas inom de närmaste 10 minuterna. 10:22

    Kontanter: Ja! 10:22

    Sida: Tack! 10:22

    Sida: Hej, jag behöver "/article/", "/script.js" och "/styles.css" igen 10:28

    Cash: Hoppsan, jag är ledsen, men jag förlorade "/styles.css", men jag har allt annat, varsågod. Server, kan du anpassa "/styles.css" åt mig? 10:28

    Server: Lätt, han har redan förändrats sedan du senast tog honom. Du kan säkert använda den under de kommande 10 minuterna. 10:29

    Kontanter: Inga problem. 10:29

    Sida: Tack! Men det verkar som att något gick fel! Allt är trasigt! Vad händer? 10:29

    Detta mönster har rätt till liv under testning, men det bryter allt i ett riktigt projekt och är väldigt svårt att spåra. I exemplet ovan har servern uppdaterat HTML, CSS och JS, men sidan renderas med den gamla cachade HTML och JS, plus den uppdaterade CSS från servern. Versionsfel förstör allt.

    När vi gör betydande ändringar i HTML ändrar vi ofta både CSS för att korrekt återspegla den nya strukturen och JavaScript för att hålla jämna steg med innehållet och stilen. Alla dessa resurser är oberoende, men cachinghuvuden kan inte uttrycka detta. Som ett resultat kan användare hitta sig själva senaste versionen en/två resurser och den gamla versionen av resten.

    max-age ställs in i förhållande till svarstiden, så om alla resurser överförs som en del av en enda adress kommer de att löpa ut samtidigt, men det finns fortfarande en liten chans till avsynkronisering. Om du har sidor som inte inkluderar JavaScript eller inkluderar andra stilar, kommer deras cache-förfallodatum att vara osynkroniserade. Och ännu värre, webbläsaren drar ständigt innehåll från cachen, utan att veta att HTML, CSS och JS är beroende av varandra, så den kan gärna dra en sak från listan och glömma allt annat. Med tanke på alla dessa faktorer tillsammans bör du förstå att sannolikheten för felaktiga versioner är ganska hög.

    För användaren kan resultatet bli en trasig sidlayout eller andra problem. Från små fel till helt oanvändbart innehåll.

    Lyckligtvis har användare en nödutgång...

    Att uppdatera sidan hjälper ibland

    Om sidan läses in genom uppdatering utför webbläsare alltid omvalidering på serversidan och ignorerar max-age . Därför, om användaren har något trasigt på grund av max-age , kan en enkel siduppdatering fixa allt. Men, naturligtvis, efter att skedarna har hittats kommer sediment fortfarande att finnas kvar och attityden till din webbplats kommer att vara något annorlunda.

    En servicearbetare kan förlänga livslängden för dessa buggar

    Du har till exempel en tjänstearbetare så här:

    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 => ( // ...radera gamla cacher... )); self.addEventListener("fetch", event => ( event.respondWith(caches.match(event.request) .then(response => svar || fetch(event.request))); ));

    Denna servicearbetare:

    • cachar skript och stilar
    • använder cache om det finns en matchning, annars kommer åt nätverket

    Om vi ​​ändrar CSS/JS ökar vi även versionsnumret, vilket utlöser en uppdatering. Men eftersom addAll kommer åt cachen först, kan vi hamna i ett tävlingstillstånd på grund av maxålder och felaktiga CSS- och JS-versioner.

    När de väl har cachelagrats kommer vi att ha inkompatibla CSS & JS tills nästa uppdatering av servicearbetaren - och det är såvida vi inte hamnar i ett racetillstånd igen under uppdateringen.

    Du kan hoppa över cachelagring i Service Worker:

    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" )) ]))); ));

    Tyvärr stöds inte alternativ för cachning i Chrome/Opera och har precis lagts till i den nattliga versionen av Firefox, men du kan göra det själv:

    Self.addEventListener("install", event => ( event.waitUntil(caches.open(`static-$(version)`) .then(cache => Promise.all([ "/styles.css", "/script) .js" ].map(url => ( // cache-bust med en slumpmässig frågesträng returnerar fetch(`$(url)?$(Math.random())`).then(response => ( // fail) på 404, 500 etc om (!response.ok) throw Error("Inte ok"); return cache.put(url, response); )) )))))); ));

    I det här exemplet återställer jag cachen med ett slumptal, men du kan gå längre och lägga till en hash av innehållet när du bygger (detta liknar vad sw-precache gör). Detta är en slags implementering av det första mönstret med JavaScript, men fungerar bara med tjänstearbetaren, inte webbläsare och CDN.

    Servicearbetare och HTTP-cache fungerar utmärkt tillsammans, få dem inte att slåss!

    Som du kan se kan du kringgå cachingbuggarna i din serviceworker, men det är bättre att lösa roten till problemet. Rätt inställning cachelagring gör inte bara servicearbetarens jobb enklare, utan hjälper också webbläsare som inte stöder servicearbetare (Safari, IE/Edge), och låter dig också få ut det mesta av ditt CDN.

    Korrekt cachningshuvud kan också göra det mycket enklare att uppdatera en servicearbetare.

    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" ]))); ));

    Här cachade jag rotsidan med mönster #2 (förnyelsevalidering på serversidan) och alla andra resurser med mönster #1 (oföränderligt innehåll). Varje service worker-uppdatering kommer att orsaka en begäran till rotsidan, och alla andra resurser kommer bara att laddas om deras URL har ändrats. Detta är bra eftersom det sparar trafik och förbättrar prestandan, oavsett om du uppgraderar från en tidigare eller mycket gammal version.

    Det finns en betydande fördel här jämfört med den ursprungliga implementeringen, när hela binären laddas ner även med en liten förändring eller orsakar en komplex jämförelse binära filer. På så sätt kan vi uppdatera en stor webbapplikation med en relativt liten belastning.

    Servicearbetare fungerar bättre som en förbättring snarare än en tillfällig krycka, så arbeta med cachen istället för att bekämpa den.

    Vid försiktig användning kan max-age och variabelt innehåll vara mycket bra

    max-age är väldigt ofta fel val för föränderligt innehåll, men inte alltid. Till exempel har originalartikeln en maxålder på tre minuter. Rastillståndet är inte ett problem eftersom det inte finns några beroenden på sidan som använder samma cachingmönster (CSS, JS & bilder använder mönster #1 - oföränderligt innehåll), allt annat använder inte detta mönster.

    Det här mönstret innebär att jag bekvämt kan skriva en populär artikel, och min CDN (Cloudflare) kan ta belastningen från servern, så länge jag är villig att vänta tre minuter på att den uppdaterade artikeln blir tillgänglig. tillgängliga för användarna.

    Detta mönster bör användas utan fanatism. Om jag lade till ett nytt avsnitt i en artikel och länkade till det från en annan artikel, skapade jag ett beroende som måste lösas. Användaren kan klicka på länken och få en kopia av artikeln utan önskat avsnitt. Om jag vill undvika detta bör jag uppdatera artikeln, ta bort den cachade versionen av artikeln från Cloudflare, vänta tre minuter och först sedan lägga till länken till en annan artikel. Ja, detta mönster kräver försiktighet.

    När den används på rätt sätt ger cachelagring betydande prestandaförbättringar och bandbreddsbesparingar. Servera oföränderligt innehåll om du enkelt kan ändra webbadressen, eller använd omvalidering på serversidan. Blanda max-age och föränderligt innehåll om du är modig nog och säker på att ditt innehåll inte har beroenden som kan hamna ur synk.

    När vi gör ändringar på webbplatser stöter vi ofta på det faktum att innehållet på sidor, css-filer och skript (.js) cachelagras av webbläsaren och förblir oförändrade under ganska lång tid. Detta leder till det faktum att för att ändringarna som görs ska återspeglas i alla webbläsare, är det nödvändigt att vänja klienter till komplexa kombinationer av F5 eller Ctrl + F5. Och se då och då till att de är pressade.

    Processen är ganska tråkig och obekväm. Du kan naturligtvis komma ur situationen genom att byta namn på filerna varje gång, men återigen är det obekvämt.

    Det finns dock ett sätt som gör att vi kan behålla samma namn och återställa cachen för .css- eller .js-filer i det ögonblick vi behöver det. Och glöm Ctrl + F5 för alltid.

    Summan av kardemumman är att vi kommer att bifoga en pseudoparameter till våra .css- eller .js-filer i slutet, som vi kommer att ändra då och då, och därigenom återställa cachen i webbläsaren.

    Alltså inträdet i källkod kommer nu se ut så här:

    Där 186485 är en godtycklig kombination som kommer att mata ut samma fil, men webbläsaren tolkar den som ny, tack vare pseudoparametern ?186485

    Nu, för att inte ändra alla förekomster av vår parameter varje gång, kommer vi att ställa in den i en php-fil, som vi kommer att ansluta till alla platser vi behöver: