Come forzare il browser a ricaricare i file CSS / JS memorizzati nella cache?


993

Ho notato che alcuni browser (in particolare Firefox e Opera) sono molto zelanti nell'uso di copie cache dei file .css e .js , anche tra le sessioni del browser. Ciò comporta un problema quando si aggiorna uno di questi file ma il browser dell'utente continua a utilizzare la copia memorizzata nella cache.

La domanda è: qual è il modo più elegante di forzare il browser dell'utente a ricaricare il file quando è cambiato?

Idealmente, la soluzione non forzerebbe il browser a ricaricare il file ad ogni visita alla pagina. Pubblicherò la mia soluzione come risposta, ma sono curioso di sapere se qualcuno ha una soluzione migliore e lascerò decidere i vostri voti.

Aggiornare :

Dopo aver permesso la discussione qui per un po ', ho trovato utile il suggerimento di John Millikin e da5id . Si scopre che esiste un termine per questo: auto-versioning .

Di seguito ho pubblicato una nuova risposta che è una combinazione della mia soluzione originale e del suggerimento di John.

Un'altra idea suggerita da SCdF sarebbe quella di aggiungere una stringa di query fasulla al file. (Alcuni codici Python per utilizzare automaticamente il timestamp come stringa di query fasulla sono stati inviati da pi .). Tuttavia, si discute se il browser memorizzerebbe nella cache un file con una stringa di query. (Ricorda, vogliamo che il browser memorizzi nella cache il file e lo utilizzi nelle visite future. Vogliamo che recuperi nuovamente il file solo quando è cambiato.)

Dato che non è chiaro cosa accada con una stringa di query fasulla, non accetto questa risposta.


Ho questo nel mio .htaccess, e mai problemi con i file memorizzati nella cache: ExpiresActive On ExpiresDefault "modification".
Frank Conijn,

2
Sono assolutamente d'accordo sul fatto che l'aggiunta di informazioni sulla versione all'URL del file sia di gran lunga il modo migliore per procedere. Funziona sempre per tutti. Ma, se non lo stai usando, e devi solo ricaricare quel file CSS o JS di tanto in tanto nel tuo browser ... basta aprirlo nella propria scheda e premere SHIFT-reload (o CTRL-F5)! Puoi fare esattamente la stessa cosa usando JS caricando un file in un iframe (nascosto), aspettando che si carichi, quindi chiamando iframe.contentWindow.location.reload(true). Vedi il metodo (4) di stackoverflow.com/a/22429796/999120 - si tratta di immagini, ma lo stesso vale.
Doin,

2
Apprezzo molto il modo in cui questa domanda è stata posta e da allora è stata aggiornata. Descriveva completamente cosa avrei dovuto aspettarmi dalle risposte. D'ora in poi seguirò questo approccio nelle mie domande. Saluti!
rd22

Risposte:


455

Aggiornamento: riscritto per includere suggerimenti di John Millikin e da5id . Questa soluzione è scritta in PHP, ma dovrebbe essere facilmente adattata ad altre lingue.

Aggiornamento 2: Incorporando i commenti di Nick Johnson che il .htaccessregex originale può causare problemi con file come json-1.3.js. La soluzione è riscrivere solo se ci sono esattamente 10 cifre alla fine. (Perché 10 cifre coprono tutti i timestamp dal 09/09/2001 al 20/11/2286).

Innanzitutto, usiamo la seguente regola di riscrittura in .htaccess:

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

Ora scriviamo la seguente funzione PHP:

/**
 *  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);
}

Ora, ovunque includi il tuo CSS, modificalo da questo:

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

A questo:

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

In questo modo, non dovrai mai più modificare il tag link e l'utente vedrà sempre l'ultimo CSS. Il browser sarà in grado di memorizzare nella cache il file CSS, ma quando si apportano modifiche al CSS, il browser lo vedrà come un nuovo URL, quindi non utilizzerà la copia memorizzata nella cache.

Questo può funzionare anche con immagini, favicon e JavaScript. Fondamentalmente tutto ciò che non viene generato dinamicamente.


16
Il mio server di contenuti statici fa esattamente lo stesso, tranne per il fatto che uso un parametro per il controllo delle versioni (base.css? V = 1221534296) anziché una modifica del nome file (base.1221534296.css). Sospetto che la tua strada potrebbe essere un po 'più efficiente. Molto bello.
Jens Roland,

4
@Kip: soluzione molto elegante. La riscrittura degli URL ha ovviamente molto di più da offrire oltre ai semplici URL.
James P.

37
Vedo un problema con questo, che accede al filesystem molte volte - esattamente - numero di collegamenti * numero di richieste / sec ... che potrebbe o meno essere un problema per te.
Tomáš Fejfar,

3
@AlixAxel: No, i browser lo recupereranno quando il parametro cambia, ma alcuni proxy pubblici non memorizzano nella cache i file con i parametri url, quindi la migliore pratica è quella di includere la versione nel percorso. E l'overhead di mod_rewrite è minimo rispetto a tutti gli altri colli di bottiglia delle prestazioni in WPO
Jens Roland,

8
Il primo file_existscontrollo è davvero necessario? filemtimerestituirà false in caso di errore, quindi perché non assegnare semplicemente il valore filemtime a una variabile e verificare se è falso prima di rinominare il file? Ciò ridurrebbe un'operazione di file non necessaria che si sommerebbe davvero.
Gavin,

184

Semplice tecnica lato client

In generale, la memorizzazione nella cache è buona .. Quindi ci sono un paio di tecniche, a seconda che tu stia risolvendo il problema da solo mentre sviluppi un sito Web o se stai cercando di controllare la cache in un ambiente di produzione.

I visitatori generici del tuo sito Web non avranno la stessa esperienza che stai vivendo durante lo sviluppo del sito. Poiché il visitatore medio arriva al sito meno frequentemente (forse solo poche volte al mese, a meno che tu non sia un Google o reti hi5), è meno probabile che abbia i tuoi file nella cache e ciò potrebbe essere sufficiente. Se si desidera forzare una nuova versione nel browser, è sempre possibile aggiungere una stringa di query alla richiesta e aumentare il numero di versione quando si apportano modifiche importanti:

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

Questo assicurerà che tutti ottengano il nuovo file. Funziona perché il browser esamina l'URL del file per determinare se ha una copia nella cache. Se il tuo server non è impostato per eseguire alcuna operazione con la stringa di query, verrà ignorato, ma il nome apparirà come un nuovo file nel browser.

D'altra parte, se stai sviluppando un sito Web, non vuoi cambiare il numero di versione ogni volta che salvi una modifica alla tua versione di sviluppo. Sarebbe noioso.

Quindi, mentre stai sviluppando il tuo sito, un buon trucco sarebbe generare automaticamente un parametro della stringa di query:

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

L'aggiunta di una stringa di query alla richiesta è un buon modo per eseguire la versione di una risorsa, ma per un semplice sito Web potrebbe non essere necessario. E ricorda, la memorizzazione nella cache è una buona cosa.

Vale anche la pena notare che il browser non è necessariamente avaro nel mantenere i file nella cache. I browser hanno criteri per questo genere di cose e di solito giocano secondo le regole stabilite nella specifica HTTP. Quando un browser invia una richiesta a un server, parte della risposta è un'intestazione EXPIRES .. una data che indica al browser per quanto tempo deve essere conservato nella cache. La prossima volta che il browser incontra una richiesta per lo stesso file, vede che ne ha una copia nella cache e osserva la data di SCADENZA per decidere se debba essere usato.

Quindi, che ci crediate o no, in realtà è il vostro server a rendere tale cache del browser così persistente. È possibile regolare le impostazioni del server e modificare le intestazioni EXPIRES, ma la piccola tecnica che ho scritto sopra è probabilmente un modo molto più semplice per farlo. Poiché la memorizzazione nella cache è buona, di solito si desidera impostare tale data in un futuro (una "Intestazione di scadenza molto futura") e utilizzare la tecnica sopra descritta per forzare una modifica.

Se sei interessato a maggiori informazioni su HTTP o su come vengono fatte queste richieste, un buon libro è "Siti Web ad alte prestazioni" di Steve Souders. È un'ottima introduzione all'argomento.


3
Il trucco rapido di generare una stringa di query con Javascript funziona perfettamente durante lo sviluppo attivo. Ho fatto la stessa cosa con PHP.
Alan Turing,

2
Questo è il modo più semplice per ottenere il risultato desiderato del poster originale. Il metodo mod_rewrite funziona bene se si desidera forzare un ricaricamento del file .css o .js OGNI volta che si carica la pagina. Questo metodo consente ancora la memorizzazione nella cache fino a quando non si modifica effettivamente il file e si vuole davvero forzare il ricaricamento.
scott80109,

@keparo, ho un ampio numero di jquery in tutte le pagine se ho intenzione di cambiarlo manualmente ci vorrà un mese. Se puoi aiutarmi a risolvere tutto senza scrivere codice per ogni pagina.
cracker,

1
Questo non sembra funzionare per il mio CSS quando uso:<link href='myCss.css?dev=14141'...>
Noumenon

3
Questa non è una soluzione praticabile. Un buon numero di browser semplicemente rifiuterà di memorizzare nella cache qualsiasi cosa con una stringa di query su di essa. Questo è il motivo per cui Google, GTMetrix e strumenti simili solleveranno un flag se si hanno stringhe di query su riferimenti a contenuto statico. Sebbene sia certamente una soluzione decente per lo sviluppo, non è assolutamente una soluzione per la produzione. Inoltre, il browser controlla la memorizzazione nella cache, non il server. Il server SUGGERISCE semplicemente quando dovrebbe essere aggiornato; un browser NON DEVE ascoltare il server (e spesso non lo fa). I dispositivi mobili ne sono un ottimo esempio.
Nate I,

113

Il plug-in mod_pagespeed di Google per apache eseguirà la versione automatica per te. È davvero lucido.

Analizza HTML uscendo dal server web (funziona con PHP, rails, python, HTML statico - qualsiasi cosa) e riscrive i collegamenti a file CSS, JS, immagine in modo che includano un codice ID. Fornisce i file agli URL modificati con un controllo cache molto lungo su di essi. Quando i file cambiano, cambia automaticamente gli URL, quindi il browser deve recuperarli. Fondamentalmente funziona, senza alcuna modifica al codice. Minimizzerà anche il tuo codice all'uscita.


1
Fantastico, ma ancora in beta. Può essere utilizzato per il servizio aziendale?
Sanghyun Lee,

26
Questo è SBAGLIATO (armeggiare automaticamente con la fonte) quando si tratta chiaramente di un problema del browser. Dacci (sviluppatori) un vero aggiornamento di pulizia del cervello: <ctrl> + F5
T4NK3R

25
mod_pagespeed è funzionalmente equivalente a un passaggio di compilazione / compilazione completamente automatico per il tuo html / css / js. Penso che sarebbe difficile trovare sviluppatori seri che pensano che i sistemi di compilazione siano intrinsecamente sbagliati o che ci sia qualcosa di sbagliato nel fatto che sia completamente automatico. L'analogia di una build pulita è cancellare la cache di mod_pagespeed: code.google.com/p/modpagespeed/wiki/… ?
Leopd,

3
@ T4NK3R mod_pagespeed non deve fare nulla con il tuo sorgente per eseguire la gestione della cache, è stato semplicemente detto che può aiutare con cose come la minimizzazione. Se sia "SBAGLIATO", completamente soggettivo. Potrebbe essere sbagliato per te, ma ciò non significa che sia instirinsically male .
Madbreaks

2
Funziona anche con nginx anche se devi crearlo dalla fonte: developers.google.com/speed/pagespeed/module/…
Rohit

93

Invece di modificare manualmente la versione, ti consiglio di utilizzare un hash MD5 del file CSS effettivo.

Quindi il tuo URL sarebbe qualcosa di simile

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

È comunque possibile utilizzare la regola di riscrittura per eliminare l'hash, ma il vantaggio è che ora è possibile impostare il criterio di cache su "cache per sempre", poiché se l'URL è lo stesso, significa che il file è invariato.

Puoi quindi scrivere un semplice script di shell che calcoli l'hash del file e aggiorni il tuo tag (probabilmente vorrai spostarlo in un file separato per l'inclusione).

Esegui semplicemente quello script ogni volta che cambia CSS e sei a posto. Il browser ricaricherà i tuoi file SOLO quando vengono modificati. Se fai una modifica e poi la annulli, non c'è alcun problema nel capire a quale versione devi tornare affinché i tuoi visitatori non possano scaricare di nuovo.


1
purtroppo non so come implementarlo. Consiglio per favore ... maggiori dettagli ...
Michael Phelps,

Un'implementazione in shell, ruby, ecc. Sarebbe fantastica
Peter,

3
Soluzione molto bella .. ma penso che sia un dispendio di risorse per calcolare l'hash del file in ogni richiesta di file (css, js, images, html..etc) per ogni singola visita di pagina.
DeepBlue,

Questa è una soluzione standard per coloro che usano js o css in bundle con gulp, grunt o webpack, l'implementazione differisce per ogni soluzione, ma l'hashing dei tuoi file come fase di costruzione è comune e suggerito per le moderne app in bundle
Brandon Søren Culley

@DeepBlue - la risposta dice "esegui quello script ogni volta che cambia CSS" . Questo NON è su ogni visita alla pagina. OTOH La risposta tralascia i dettagli principali: come l'hash modificato diventa parte dell'URL? Non lo so ...
ToolmakerSteve

71

Non sono sicuro del motivo per cui state provando così tanto dolore per implementare questa soluzione.

Tutto quello che devi fare se ottieni il timestamp modificato del file e aggiungilo come stringa di query al file

In PHP lo farei come:

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

filemtime è una funzione PHP che restituisce il timestamp del file modificato.


Puoi semplicemente usare mycss.css?1234567890.
Gavin,

3
molto elegante, anche se l'ho leggermente modificato <link rel="stylesheet" href="mycss.css?<?php echo filemtime('mycss.css') ?>"/>, nel caso in cui alcuni degli argomenti di questo thread sulla memorizzazione nella cache degli URL con variabili GET (nel formato suggerito) siano corretti
luke_mclachlan

oltre al mio ultimo commento, ho visto che wordpress usa ?ver=così chi lo sa!
luke_mclachlan,

Ottima soluzione Inoltre, ho scoperto che filemtime non funzionava con un nome di dominio completo (FQDN), quindi ho usato l'FQDN per la parte href e $ _SERVER ["DOCUMENT_ROOT"] per la parte filemtime. EX: <link rel = "stylesheet" href = "http: //theurl/mycss.css? V = <? Php echo filemtime ($ _ SERVER [" DOCUMENT_ROOT "]. '/Mycss.css')?>" />
rrtx2000,

Grazie mille. Semplice e buono. Eccolo in Python: progpath = os.path.dirname (sys.argv [0]) def versionize (file): timestamp = os.path.getmtime ('% s /../ web /% s'% (progpath , file)) return '% s? v =% s'% (file, timestamp) print <link href = "% s" rel = "stylesheet" '' type = "text / css" /> '\% versionize ( 'css / main.css')
dlink

52

Puoi semplicemente mettere ?foo=1234alla fine dell'importazione css / js, cambiando 1234 per essere quello che ti piace. Dai un'occhiata alla fonte html SO per un esempio.

L'idea che ci sia quella? i parametri vengono comunque scartati / ignorati nella richiesta e puoi cambiare quel numero quando lanci una nuova versione.


Nota: vi sono alcuni argomenti in merito al modo in cui ciò influisce sulla memorizzazione nella cache. Credo che l'essenza generale sia che le richieste GET, con o senza parametri, debbano essere accettabili, quindi la soluzione sopra dovrebbe funzionare.

Tuttavia, spetta sia al server Web decidere se desidera aderire a quella parte delle specifiche sia al browser utilizzato dall'utente, in quanto può semplicemente andare avanti e chiedere comunque una nuova versione.


Senza senso. La stringa di query (ovvero parametri GET) fa parte dell'URL. Possono e saranno memorizzati nella cache. Questa è una buona soluzione
troelskn,

9
@troelskn: Le specifiche HTTP 1.1 dicono diversamente (rispetto alle richieste GET e HEAD con parametri di query): le cache NON DEVONO trattare le risposte a tali URI come nuove a meno che il server non fornisca un tempo di scadenza esplicito. Vedi w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9
Michael Johnson,

4
Ho provato il tipo di stringa di query di versioning con tutti i principali browser e NON memorizzano nella cache il file, le specifiche o meno. Tuttavia, penso che sia meglio usare il formato style.TIMESTAMP.css senza abusare comunque delle stringhe di query perché c'è ancora la possibilità che la cache del software proxy NON memorizzi nella cache il file.
Tomas Andrle,

34
Vale la pena notare, per qualsiasi motivo, che Stackoverflow stesso utilizza il metodo della stringa di query.
Jason,

2
Hai verificato che l'utilizzo del parametro? = Non consentirà ai browser di recuperare nuovamente il file memorizzato nella cache quando il parametro cambia. L'unico modo è cambiare il nome del file stesso a livello di programmazione all'estremità del server, come ha risposto Kip
arunskrish,

41

Ho sentito questo chiamato "versioning automatico". Il metodo più comune è includere il mtime del file statico da qualche parte nell'URL e rimuoverlo usando i gestori di riscrittura o le configurazioni URL:

Guarda anche:


3
Grazie, immagino che questo sia stato un altro caso in cui la mia idea è stata discussa, semplicemente non sapevo come si chiamava, quindi non l'ho mai trovata nelle ricerche di Google.
Kip

27

Le circa 30 risposte esistenti sono ottimi consigli per un sito web del 2008 circa. Tuttavia, quando si tratta di un'applicazione moderna a singola pagina (SPA), potrebbe essere il momento di ripensare alcune ipotesi fondamentali ... in particolare l'idea che è auspicabile che il server Web serva solo la versione singola e più recente di un file.

Immagina di essere un utente con la versione M di una SPA caricata nel tuo browser:

  1. La pipeline del CD distribuisce la nuova versione N dell'applicazione sul server
  2. Si naviga all'interno della SPA, che invia un XHR al server per ottenere /some.template
    • (Il tuo browser non ha aggiornato la pagina, quindi stai ancora eseguendo la versione M )
  3. Il server risponde con il contenuto di /some.template: vuoi che restituisca la versione M o N del modello?

Se il formato è /some.templatecambiato tra le versioni M e N (o il file è stato rinominato o altro) probabilmente non vuoi che la versione N del modello sia inviata al browser che esegue la vecchia versione M del parser . †

Le app Web riscontrano questo problema quando si verificano due condizioni:

  • Le risorse vengono richieste in modo asincrono dopo il caricamento iniziale della pagina
  • La logica dell'app presuppone elementi (che potrebbero cambiare nelle versioni future) sul contenuto delle risorse

Quando l'app deve pubblicare più versioni in parallelo, la risoluzione della cache e il "ricaricamento" diventano banali:

  1. Installare tutti i file del sito in dirs di versione: /v<release_tag_1>/…files…,/v<release_tag_2>/…files…
  2. Imposta le intestazioni HTTP per consentire ai browser di memorizzare nella cache i file per sempre
    • (O meglio ancora, metti tutto in un CDN)
  3. Aggiornare tutti <script>e <link>tag, ecc a punto a tale file in uno dei dirs con versione

L'ultimo passaggio sembra complicato, poiché potrebbe richiedere la chiamata di un generatore di URL per ogni URL nel codice lato server o lato client. Oppure potresti semplicemente fare un uso intelligente del <base>tag e modificare la versione corrente in un unico posto.

† Un modo per aggirare questo è di essere aggressivo nel forzare il browser a ricaricare tutto quando viene rilasciata una nuova versione. Ma per consentire il completamento di qualsiasi operazione in corso, potrebbe essere ancora più semplice supportare almeno due versioni in parallelo: v-current e v-previous.


Michael - il tuo commento è molto pertinente. Sono venuto qui proprio cercando di trovare una soluzione per la mia SPA. Ho avuto alcuni suggerimenti, ma ho dovuto trovare una soluzione me stesso. Alla fine, sono stato davvero contento di quello che mi è venuto in mente, quindi ho scritto un post sul blog e una risposta a questa domanda (incluso il codice). Grazie per i suggerimenti
Statler

Ottimo commento Non riesco a capire mentre le persone continuano a parlare del busting della cache e della memorizzazione nella cache HTTP come la vera soluzione ai problemi di memorizzazione nella cache dei siti Web senza accennare ai nuovi problemi delle SPA, come se questo fosse un caso marginale.
David Casillas,

1
Risposta eccellente e strategia assolutamente ideale! E punti bonus per menzionare il basetag! Per quanto riguarda il supporto del vecchio codice: questa non è sempre una possibilità, né è sempre una buona idea. Le nuove versioni di codice possono supportare la rottura di altre parti di un'app o comportare correzioni di emergenza, patch di vulnerabilità e così via. Devo ancora implementare questa strategia da solo, ma ho sempre pensato che l'architettura generale dovrebbe consentire alle distribuzioni di taggare una vecchia versione obsoletee forzare un ricaricamento la prossima volta che viene effettuata una chiamata asincrona (o semplicemente annullare l' autorizzazione forzata di tutte le sessioni tramite WebSocket ).
Jonny Asmar il

Bello vedere una risposta ben ponderata per quanto riguarda le applicazioni a pagina singola.
Nate I

Questa è "distribuzione blu-verde" se si desidera cercare ulteriori informazioni.
Fil

15

Non usare foo.css? Versione = 1! I browser non devono memorizzare nella cache URL con variabili GET. Secondo http://www.thinkvitamin.com/features/webapps/serving-javascript-fast , sebbene IE e Firefox lo ignorino, Opera e Safari no! Utilizzare invece foo.v1234.css e utilizzare le regole di riscrittura per eliminare il numero di versione.


1
Prima di tutto i browser non memorizzano nella cache, questa è una funzione di HTTP. Perché http dovrebbe preoccuparsi della struttura di un URI? Esiste un riferimento ufficiale a una specifica che afferma che la cache HTTP dovrebbe comprendere la semantica di un URI in modo che non memorizzi nella cache elementi con una stringa di query?
AnthonyWJones,

13
Un browser Web che include la funzionalità di memorizzazione nella cache degli oggetti (controlla la directory della cache del browser). HTTP è un protocollo che include le direttive dai server ai client (proxy, browser, spider ecc.) Che suggeriscono il controllo della cache.
tzot

13

In Laravel (PHP) possiamo farlo nel seguente modo chiaro ed elegante (usando il timestamp di modifica dei file):

<script src="{{ asset('/js/your.js?v='.filemtime('js/your.js')) }}"></script>

E simile per CSS

<link rel="stylesheet" href="{{asset('css/your.css?v='.filemtime('css/your.css'))}}">

Esempio di output HTML ( filemtimetempo di ritorno come data / ora Unix )

<link rel="stylesheet" href="assets/css/your.css?v=1577772366">

qual è l'output di questo comando in html? E se avessi bisogno di rinnovare solo versioni come? V = 3,? V = 4 ed ecc. - Non impone al browser di caricare i CSS ogni volta che l'utente accede al sito Web
Gediminas,

filemtime : "Questa funzione restituisce l'ora in cui i blocchi di dati di un file venivano scritti, ovvero l'ora in cui il contenuto del file è stato modificato." src: php.net/manual/en/function.filemtime.php
Kamil Kielczewski

11

RewriteRule necessita di un piccolo aggiornamento per i file js o css che contengono un controllo delle versioni delle notazioni punto alla fine. Ad esempio json-1.3.js.

Ho aggiunto una classe di negazione del punto [^.] Al regex così .number. viene ignorato.

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

2
Grazie per l'input! Da quando ho scritto questo post sono stato bruciato anche da questo. La mia soluzione era riscrivere solo se l'ultima parte del nome file contiene esattamente dieci cifre. (10 cifre coprono tutti i timestamp dal 09/09/2001 al 20/11/2286). Ho aggiornato la mia risposta per includere questa regex:^(.*)\.[\d]{10}\.(css|js)$ $1.$2
Kip

Capisco regex, ma non capisco con quale problema stai risolvendo [^.]qui. Inoltre, non c'è alcun vantaggio nello scrivere \dall'interno di una classe di personaggi: \d+farà la stessa cosa. Come pubblicato, il tuo modello corrisponderà a qualsiasi numero di caratteri (avidamente), quindi un punto letterale, quindi un non punto, quindi una o più cifre, quindi un punto, quindi csso js, quindi la fine del nome file. Nessuna corrispondenza per il tuo input di esempio: regex101.com/r/RPGC62/1
mickmackusa

10

Per ASP.NET 4.5 e versioni successive è possibile utilizzare il bundle di script .

La richiesta http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81è per il pacchetto AllMyScripts e contiene una coppia di stringhe di query v = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81. La stringa di query v ha un token di valore che è un identificatore univoco utilizzato per la memorizzazione nella cache. Finché il bundle non cambia, l'applicazione ASP.NET richiederà il bundle AllMyScripts utilizzando questo token. Se qualsiasi file nel bundle cambia, il framework di ottimizzazione ASP.NET genererà un nuovo token, garantendo che le richieste del browser per il bundle ottengano l'ultimo bundle.

Ci sono altri vantaggi del raggruppamento, tra cui un aumento delle prestazioni al primo caricamento della pagina con minificazione.


Aiutatemi, non sto apportando alcuna modifica a bundle.config semplicemente cambiando i file CSS o JS, quindi come posso risolvere il problema di memorizzazione nella cache?
vedankita kumbhar,

10

Ecco una soluzione JavaScript pura

(function(){

    // Match this timestamp with the release of your code
    var lastVersioning = Date.UTC(2014, 11, 20, 2, 15, 10);

    var lastCacheDateTime = localStorage.getItem('lastCacheDatetime');

    if(lastCacheDateTime){
        if(lastVersioning > lastCacheDateTime){
            var reload = true;
        }
    }

    localStorage.setItem('lastCacheDatetime', Date.now());

    if(reload){
        location.reload(true);
    }

})();

Quanto sopra cercherà l'ultima volta che l'utente ha visitato il tuo sito. Se l'ultima visita è stata prima del rilascio del nuovo codice, viene utilizzato location.reload(true)per forzare l'aggiornamento della pagina dal server.

Di solito ho questo come il primo script all'interno, <head>quindi viene valutato prima che venga caricato qualsiasi altro contenuto. Se è necessario un ricaricamento, è appena percettibile per l'utente.

Sto usando l'archiviazione locale per archiviare il timestamp dell'ultima visita sul browser, ma puoi aggiungere cookie al mix se stai cercando di supportare versioni precedenti di IE.


Ho provato qualcosa del genere, funzionerà solo sulla pagina ricaricata, ma se il sito ha più pagine che condividono gli stessi css / immagini, altre pagine useranno comunque vecchie risorse.
DeepBlue,

9

Post interessante. Avendo letto tutte le risposte qui combinate con il fatto che non ho mai avuto problemi con stringhe di query "fasulle" (che non sono sicuro del motivo per cui tutti sono così riluttanti a usarlo), credo che la soluzione (che elimina la necessità di regole di riscrittura di Apache come nella risposta accettata) è calcolare un breve HASH del contenuto del file CSS (anziché il file datetime) come una falsa stringa di query.

Ciò comporterebbe quanto segue:

<link rel="stylesheet" href="/css/base.css?[hash-here]" type="text/css" />

Ovviamente anche le soluzioni datetime svolgono il lavoro nel caso di modificare un file CSS, ma penso che si tratti del contenuto del file css e non del file datetime, quindi perché confonderli?


8

Per il mio sviluppo, trovo che Chrome abbia un'ottima soluzione.

https://developer.chrome.com/devtools/docs/tips-and-tricks#hard-reload

Con gli strumenti di sviluppo aperti, fai semplicemente clic sul pulsante di aggiornamento e rilascia una volta che passi il mouse su "Svuota cache e Ricarica dura".

Questo è il mio migliore amico ed è un modo super leggero per ottenere quello che vuoi!


E se stai usando Chrome come ambiente di sviluppo, un'altra soluzione non invasiva è quella di disabilitare la cache: Sotto il cog delle Impostazioni, puoi invalidare la cache del disco selezionando 'Disabilita cache' (nota: DevTools deve essere visibile / aperto affinché funzioni).
Velojet,

7

Grazie a Kip per la sua soluzione perfetta!

L'ho esteso per usarlo come Zend_view_Helper. Poiché il mio client esegue la sua pagina su un host virtuale, l'ho estesa anche per questo.

Spero che aiuti anche qualcun altro.

/**
 * Extend filepath with timestamp to force browser to
 * automatically refresh them if they are updated
 *
 * This is based on Kip's version, but now
 * also works on virtual hosts
 * @link http://stackoverflow.com/questions/118884/what-is-an-elegant-way-to-force-browsers-to-reload-cached-css-js-files
 *
 * Usage:
 * - extend your .htaccess file with
 * # Route for My_View_Helper_AutoRefreshRewriter
 * # which extends files with there timestamp so if these
 * # are updated a automatic refresh should occur
 * # RewriteRule ^(.*)\.[^.][\d]+\.(css|js)$ $1.$2 [L]
 * - then use it in your view script like
 * $this->headLink()->appendStylesheet( $this->autoRefreshRewriter($this->cssPath . 'default.css'));
 *
 */
class My_View_Helper_AutoRefreshRewriter extends Zend_View_Helper_Abstract {

    public function autoRefreshRewriter($filePath) {

        if (strpos($filePath, '/') !== 0) {

            // path has no leading '/'
            return $filePath;
        } elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . $filePath)) {

            // file exists under normal path
            // so build path based on this
            $mtime = filemtime($_SERVER['DOCUMENT_ROOT'] . $filePath);
            return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
        } else {

            // fetch directory of index.php file (file from all others are included)
            // and get only the directory
            $indexFilePath = dirname(current(get_included_files()));

            // check if file exist relativ to index file
            if (file_exists($indexFilePath . $filePath)) {

                // get timestamp based on this relativ path
                $mtime = filemtime($indexFilePath . $filePath);

                // write generated timestamp to path
                // but use old path not the relativ one
                return preg_replace('{\\.([^./]+)$}', ".$mtime.\$1", $filePath);
            } else {

                return $filePath;
            }
        }
    }

}

Saluti e grazie.


7

Non ho trovato l'approccio DOM lato client creando dinamicamente l'elemento nodo di script (o css):

<script>
    var node = document.createElement("script"); 
    node.type = "text/javascript";
    node.src = 'test.js?'+Math.floor(Math.random()*999999999);
    document.getElementsByTagName("head")[0].appendChild(node);
</script>

6

Google Chrome ha rigido Ricarica così come Empty Cache e Hard Ricarica option.You può cliccare e tenere premuto il pulsante di ricarica (In Ispezionare Mode) per selezionare uno.


Per chiarire, da "Inspect Mode", si riferiscono a "strumenti di sviluppo" aka F12, alias ctrl + shift + i, alias ant menu> More Tools> Developer Tools, alias right click> Inspect Element. C'è anche un'impostazione sepolta da qualche parte negli strumenti di sviluppo (ho dimenticato la posizione) per ricaricare duramente ogni ricarica.
Jonny Asmar il

5

È possibile forzare una "memorizzazione nella cache a livello di sessione" se si aggiunge l'id di sessione come parametro spureous del file js / css:

<link rel="stylesheet" src="myStyles.css?ABCDEF12345sessionID" />
<script language="javascript" src="myCode.js?ABCDEF12345sessionID"></script>

Se si desidera una cache a livello di versione, è possibile aggiungere del codice per stampare la data del file o simili. Se stai usando Java puoi usare un tag personalizzato per generare il link in modo elegante.

<link rel="stylesheet" src="myStyles.css?20080922_1020" />
<script language="javascript" src="myCode.js?20080922_1120"></script>

5

Supponi di avere un file disponibile su:

/styles/screen.css

puoi aggiungere un parametro di query con le informazioni sulla versione all'URI, ad esempio:

/styles/screen.css?v=1234

oppure puoi anteporre informazioni sulla versione, ad esempio:

/v/1234/styles/screen.css

IMHO il secondo metodo è migliore per i file CSS perché possono fare riferimento a immagini usando URL relativi, il che significa che se si specifica un background-imagesimile in questo modo:

body {
    background-image: url('images/happy.gif');
}

il suo URL sarà effettivamente:

/v/1234/styles/images/happy.gif

Ciò significa che se si aggiorna il numero di versione utilizzato, il server lo tratterà come una nuova risorsa e non utilizzerà una versione memorizzata nella cache. Se si basa il numero di versione su Subversion / CVS / ecc. revisione significa che verranno notate le modifiche alle immagini a cui si fa riferimento nei file CSS. Ciò non è garantito con il primo schema, ovvero l'URL images/happy.gifrelativo a /styles/screen.css?v=1235è/styles/images/happy.gif che non contiene alcuna informazione sulla versione.

Ho implementato una soluzione di memorizzazione nella cache utilizzando questa tecnica con servlet Java e gestisco semplicemente le richieste /v/*con un servlet che delega alla risorsa sottostante (ovvero /styles/screen.css). In modalità di sviluppo ho impostato il caching intestazioni che raccontano al cliente di controllare sempre la freschezza della risorsa con il server (questo si traduce in genere in un 304 se si delega a Tomcat di DefaultServlete .css, .js, ecc file non è cambiata), mentre in modalità di implementazione Ho impostato le intestazioni che dicono "cache per sempre".


La semplice aggiunta di una cartella che è possibile rinominare quando necessario funzionerà se si utilizzano solo URL relativi. E poi hai la certezza di reindirizzare la cartella appropriata dalla cartella di base, vale a dire in PHP: <?php header( 'Location: folder1/login.phtml' ); ?>.
Gruber,

1
Utilizzando il secondo metodo, una modifica a un CSS invaliderà le copie memorizzate nella cache di tutte le immagini a cui si fa riferimento con URL relativi, il che potrebbe essere o meno desiderabile.
TomG,

5

Potresti semplicemente aggiungere un numero casuale con l'URL CSS / JS come

example.css?randomNo=Math.random()

5

Per ASP.NET suppongo che la prossima soluzione con opzioni avanzate (modalità debug / release, versioni):

File Js o Css inclusi in questo modo:

<script type="text/javascript" src="Scripts/exampleScript<%=Global.JsPostfix%>" />
<link rel="stylesheet" type="text/css" href="Css/exampleCss<%=Global.CssPostfix%>" />

Global.JsPostfix e Global.CssPostfix vengono calcolati nel modo seguente in Global.asax:

protected void Application_Start(object sender, EventArgs e)
{
    ...
    string jsVersion = ConfigurationManager.AppSettings["JsVersion"];
    bool updateEveryAppStart = Convert.ToBoolean(ConfigurationManager.AppSettings["UpdateJsEveryAppStart"]);
    int buildNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Revision;
    JsPostfix = "";
#if !DEBUG
    JsPostfix += ".min";
#endif      
    JsPostfix += ".js?" + jsVersion + "_" + buildNumber;
    if (updateEveryAppStart)
    {
        Random rand = new Random();
        JsPosfix += "_" + rand.Next();
    }
    ...
}

4

Di recente ho risolto questo problema usando Python. Ecco il codice (dovrebbe essere facile da adottare in altre lingue):

def import_tag(pattern, name, **kw):
    if name[0] == "/":
        name = name[1:]
    # Additional HTML attributes
    attrs = ' '.join(['%s="%s"' % item for item in kw.items()])
    try:
        # Get the files modification time
        mtime = os.stat(os.path.join('/documentroot', name)).st_mtime
        include = "%s?%d" % (name, mtime)
        # this is the same as sprintf(pattern, attrs, include) in other
        # languages
        return pattern % (attrs, include)
    except:
        # In case of error return the include without the added query
        # parameter.
        return pattern % (attrs, name)

def script(name, **kw):
    return import_tag("""<script type="text/javascript" """ +\
        """ %s src="/%s"></script>""", name, **kw)

def stylesheet(name, **kw):
    return import_tag('<link rel="stylesheet" type="text/css" ' +\
        """%s href="/%s">', name, **kw) 

Questo codice sostanzialmente aggiunge il timestamp dei file come parametro di query all'URL. La chiamata della seguente funzione

script("/main.css")

si tradurrà in

<link rel="stylesheet" type="text/css"  href="/main.css?1221842734">

Il vantaggio ovviamente è che non devi mai cambiare di nuovo il tuo html, toccando il file CSS attiverà automaticamente una invalidazione della cache. Funziona molto bene e il sovraccarico non è evidente.


os.stat () potrebbe creare un collo di bottiglia?
hoju,

@Richard stat potrebbe essere un collo di bottiglia se il disco è molto lento e le richieste sono moltissime. In tal caso, è possibile memorizzare nella cache il timestamp da qualche parte nella memoria ed eliminare questa cache ad ogni nuova distribuzione. Tuttavia questa complessità non sarà necessaria nella maggior parte dei casi d'uso.
pi.

4

Se stai usando git + PHP, puoi ricaricare lo script dalla cache ogni volta che c'è una modifica nel repository git, usando il seguente codice:

exec('git rev-parse --verify HEAD 2> /dev/null', $gitLog);
echo '  <script src="/path/to/script.js"?v='.$gitLog[0].'></script>'.PHP_EOL;

4

Se sei uno sviluppatore che cerca di evitare la memorizzazione nella cache, la scheda di rete Chrome ha l'opzione di disabilitazione della cache. Altrimenti puoi farlo senza un framework di rendering del server usando due tag di script.

<script type="text/javascript">
    document.write('<script type="text/javascript" src="myfile.js?q=' + Date.now() + '">
    // can't use myfile.js stuff yet
</script>')
<script type="text/javascript">
    // do something with myfile.js
</script>

4

Questa domanda è super vecchia e appare per prima cosa quando qualcuno cerca su Google questo problema. Questa non è una risposta alla domanda nel modo in cui lo desidera, ma è una risposta agli sviluppatori con questo problema durante lo sviluppo e il test. E non posso pubblicare una nuova domanda su questo argomento poiché sarà contrassegnato come duplicato.

Come molti altri, volevo solo rimuovere brevemente la cache.

"keep caching consistent with the file" .. è troppo fastidioso ..

In generale, non mi dispiace caricare di più - anche caricare di nuovo file che non sono cambiati - sulla maggior parte dei progetti - è praticamente irrilevante. Durante lo sviluppo di un'app - stiamo principalmente caricando da disco, su localhost:port - quindi questo increase in network trafficproblema non è un problema di rottura .

La maggior parte dei piccoli progetti sta solo giocando, non finisce mai in produzione. quindi per loro non hai bisogno di altro ..

Pertanto, se utilizzi Chrome Dev Tools , puoi seguire questo approccio di disabilitazione della cache come nell'immagine seguente: come forzare Chrome per ricaricare i file memorizzati nella cache

E se hai problemi di memorizzazione nella cache di Firefox : come forzare la ricarica delle risorse su Firefox

come disabilitare la memorizzazione nella cache in Firefox durante lo sviluppo Fallo solo nello sviluppo, hai anche bisogno di un meccanismo per forzare il ricaricamento per la produzione, poiché i tuoi utenti useranno i vecchi moduli invalidati della cache se aggiorni la tua app frequentemente e non fornisci un meccanismo di sincronizzazione cache dedicato come quelli descritti nelle risposte sopra.

Sì, queste informazioni sono già nelle risposte precedenti ma avevo ancora bisogno di fare una ricerca su Google per trovarle.

Spero che questa risposta sia molto chiara e ora non è necessario.


OP ha chiesto qualcosa e ha risposto qualcos'altro. Non si tratta di forzare il caricamento in locale ma in produzione e non è possibile chiedere agli utenti finali di seguire sopra per disabilitare la cache ecc.
Jitendra Pancholi

3

Sembra che tutte le risposte qui suggeriscano una sorta di versioning nello schema di denominazione, che ha i suoi lati negativi.

I browser dovrebbero essere ben consapevoli di cosa memorizzare nella cache e cosa non memorizzare nella cache leggendo la risposta dei server web, in particolare le intestazioni http - per quanto tempo è valida questa risorsa? questa risorsa è stata aggiornata dall'ultima volta che l'ho recuperata? eccetera.

Se le cose sono configurate "correttamente", l'aggiornamento dei file dell'applicazione dovrebbe (ad un certo punto) aggiornare le cache del browser. Ad esempio, puoi configurare il tuo server web per dire al browser di non memorizzare mai nella cache i file (che è una cattiva idea).

Una spiegazione più approfondita di come funziona è qui https://www.mnot.net/cache_docs/#WORK


3

Basta aggiungere questo codice, dove si desidera eseguire un ricaricamento intenso (forzare il browser a ricaricare i file CSS / JS memorizzati nella cache). Fare questo all'interno del .load in modo che non si aggiorni come un ciclo

 $( window ).load(function() {
   location.reload(true);
});

Non funziona su Chrome. Sto ancora caricando risorse dalla cache del disco
Jason Kim il

3

Basta usare il codice lato server per aggiungere la data del file ... in questo modo verrà memorizzato nella cache e ricaricato solo quando il file cambia

In ASP.NET

<link rel="stylesheet" href="~/css/custom.css?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/css/custom.css")).ToString(),"[^0-9]", ""))" />

<script type="text/javascript" src="~/js/custom.js?d=@(System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>    

Questo può essere semplificato per:

<script src="<%= Page.ResolveClientUrlUnique("~/js/custom.js") %>" type="text/javascript"></script>

Aggiungendo un metodo di estensione al progetto per estendere la Pagina:

public static class Extension_Methods
{
    public static string ResolveClientUrlUnique(this System.Web.UI.Page oPg, string sRelPath)
    {
        string sFilePath = oPg.Server.MapPath(sRelPath);
        string sLastDate = System.IO.File.GetLastWriteTime(sFilePath).ToString();
        string sDateHashed = System.Text.RegularExpressions.Regex.Replace(sLastDate, "[^0-9]", "");

        return oPg.ResolveClientUrl(sRelPath) + "?d=" + sDateHashed;
    }
}

2

Suggerisco di implementare il seguente processo:

  • versione dei file css / js ogni volta che si distribuisce, qualcosa come: screen.1233.css (il numero può essere la revisione SVN se si utilizza un sistema di controllo delle versioni)

  • minimizzali per ottimizzare i tempi di caricamento

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.