Jeg har lagt merke til at noen nettlesere (spesielt Firefox og Opera) er veldig ivrige etter å bruke bufrede kopier av .css- og .js-filer, selv mellom nettlesersesjoner. Dette fører til et problem når du oppdaterer en av disse filene, men brukerens nettleser fortsetter å bruke den bufrede kopien.
Spørsmålet er: Hva er den mest elegante måten å tvinge brukerens nettleser til å laste inn filen på nytt når den er endret?
Ideelt sett bør løsningen ikke tvinge nettleseren til å laste inn filen på nytt ved hvert besøk på siden. Jeg skal legge ut min egen løsning som svar, men jeg er nysgjerrig på om noen har en bedre løsning, så får stemmene deres avgjøre.
Oppdatering :
Etter å ha tillatt diskusjon her en stund, har jeg funnet John Millikin og da5id's forslag nyttig. Det viser seg at det finnes et begrep for dette: auto-versjonering.
Jeg har lagt ut et nytt svar nedenfor som er en kombinasjon av min opprinnelige løsning og Johns forslag.
En annen idé som ble foreslått av SCdF er å legge til en falsk spørringsstreng i filen. (En Python-kode for automatisk bruk av tidsstempelet som en falsk spørringsstreng ble foreslått av pi). Det diskuteres imidlertid hvorvidt nettleseren vil bufre en fil med en spørringsstreng. (Husk at vi ønsker at nettleseren skal bufre filen og bruke den ved fremtidige besøk. Vi vil bare at den skal hente filen på nytt når den er endret).
Siden det ikke er klart hva som skjer med en falsk spørringsstreng, godtar jeg ikke det svaret.
Oppdatering: Omskrevet for å innlemme forslag fra John Millikin og da5id. Løsningen er skrevet i PHP, men bør enkelt kunne tilpasses andre språk.
Oppdatering 2: Inkluderer kommentarer fra Nick Johnson om at den opprinnelige .htaccess
-regexen kan forårsake problemer med filer som json-1.3.js
. Løsningen er å bare omskrive hvis det er nøyaktig 10 sifre på slutten. (Fordi 10 sifre dekker alle tidsstempler fra 9/9/2001 til 20/11/2286).
Først bruker vi følgende omskrivingsregel i .htaccess:
RewriteEngine on
RewriteRule ^(.*)\.[\d]{10}\.(css|js)$ $1.$2 [L]
Nå skriver vi følgende PHP-funksjon:
/**
* 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);
}
Nå, uansett hvor du inkluderer CSS, endrer du det fra dette:
<link rel="stylesheet" href="/css/base.css" type="text/css" />
til dette:
<link rel="stylesheet" href="<?php echo auto_version('/css/base.css'); ?>" type="text/css" />
På denne måten trenger du aldri å endre koblingstaggen igjen, og brukeren vil alltid se den nyeste CSS-filen. Nettleseren vil kunne bufre CSS-filen, men når du gjør endringer i CSS-filen, vil nettleseren se dette som en ny URL, slik at den ikke bruker den bufrede kopien.
Dette kan også fungere med bilder, favicons og JavaScript. I utgangspunktet alt som ikke genereres dynamisk.
Du kan bare sette ?foo=1234
på slutten av css-/js-importen, og endre 1234 til hva du vil. Ta en titt på SO html-kilden for et eksempel.
Tanken er at ? parametrene uansett blir forkastet / ignorert i forespørselen, og at du kan endre nummeret når du lanserer en ny versjon.
Note: Det er litt uenighet om nøyaktig hvordan dette påvirker caching. Jeg tror det generelle poenget er at GET-forespørsler, med eller uten parametere, bør kunne caches, så løsningen ovenfor bør fungere.
Det er imidlertid opp til både webserveren å avgjøre om den ønsker å følge denne delen av spesifikasjonen, og nettleseren brukeren bruker, ettersom den bare kan gå rett videre og be om en ny versjon uansett.
Jeg har hørt at dette kalles "automatisk versjonering". Den vanligste metoden er å inkludere den statiske filens mtime et sted i URL-en, og fjerne den ved hjelp av rewrite handlers eller URL confs:
Se også: