it-swarm-eu.dev

Jak vynutit, aby prohlížeč znovu načetl soubory CSS/JS uložené v mezipaměti?

Všiml jsem si, že některé prohlížeče (zejména Firefox a Opera) jsou velmi horlivé v používání souborů ve formátu mezipaměti souborů .css a .js , a to i mezi relacemi prohlížeče. To vede k problému při aktualizaci jednoho z těchto souborů, ale prohlížeč uživatele nadále používá kopii uloženou v mezipaměti.

Otázkou je: co je nejelegantnější způsob, jak donutit prohlížeč uživatele znovu načíst soubor, když se změnil?

V ideálním případě by řešení nenutilo prohlížeč znovu načíst soubor při každé návštěvě stránky. Jako odpověď uvedu své vlastní řešení, ale jsem zvědavý, jestli má někdo lepší řešení a rozhodnu se o vašich hlasováních.

Aktualizace:

Poté, co jsem tu nějakou dobu umožnil diskusi, jsem našel návrh Johna Millikina a da5id být užitečný. Ukazuje se, že je to termín: auto-versioning .

Zaslal jsem novou odpověď, která je kombinací mého původního řešení a Johnova návrhu.

Další myšlenkou, kterou navrhl SCdF by bylo připojit falešný dotazovací řetězec do souboru. (Některý Python kód automaticky používá časové razítko jako falešný dotazovací řetězec byl odeslán příkazem pi .). Existuje však nějaká diskuse o tom, zda by prohlížeč ukládal do mezipaměti soubor s řetězcem dotazu. (Pamatujte, že chceme, aby prohlížeč soubor ukládal do paměti a používal ho při budoucích návštěvách. Chceme, aby soubor znovu načetl, až se změní.)

Protože není jasné, co se děje s falešným řetězcem dotazů, tuto odpověď nepřijímám.

907
Kip

Aktualizace: Přepracováno, aby obsahovalo návrhy od John Millikin a da5id . Toto řešení je napsáno v PHP, ale mělo by být snadno přizpůsobeno ostatním jazykům.

Update 2: / Začlenění komentářů z Nick Johnson že původní .htaccess regex může způsobit problémy se soubory jako json-1.3.js. Řešením je pouze přepsat, pokud je na konci přesně 10 číslic. (Protože 10 číslic zahrnuje všechny časové značky od 9/9/2001 do 11/20/2286.)

Nejprve použijeme následující pravidlo přepsání v .htaccess:

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

Nyní píšeme následující funkci 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);
}

Nyní, bez ohledu na to, kde jste zahrnuli své CSS, změňte toto z tohoto:

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

K tomuto:

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

Tímto způsobem už nikdy nebudete muset měnit značku odkazu a uživatel bude vždy vidět nejnovější CSS. Prohlížeč bude moci soubor CSS ukládat do mezipaměti, ale pokud provedete jakékoli změny v CSS, prohlížeč to uvidí jako novou adresu URL, takže nebude kopii uloženou v mezipaměti používat.

To může také pracovat s obrázky, favicons a JavaScript. V podstatě nic, co není dynamicky generováno.

427
Kip

Jednoduchá technika na straně klienta

Obecně platí, že ukládání do mezipaměti je dobré .. Takže existuje několik technik, v závislosti na tom, zda problém řešíte sami, když vyvíjíte webové stránky, nebo zda se pokoušíte ovládat mezipaměť v produkčním prostředí.

Generální návštěvníci vašich webových stránek nebudou mít stejné zkušenosti, které máte, když vyvíjíte stránky. Vzhledem k tomu, že průměrný návštěvník přichází na stránky méně často (možná jen několikrát za měsíc, pokud nejste Google nebo hi5 Networks), je menší pravděpodobnost, že budou mít vaše soubory v mezipaměti, a to může stačit. Chcete-li do prohlížeče vynutit novou verzi, můžete k požadavku vždy přidat řetězec dotazu a při provedení velkých změn vyskakovat číslo verze: 

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

To zajistí, že každý dostane nový soubor. Funguje to proto, že prohlížeč se dívá na URL souboru, aby zjistil, zda má kopii v mezipaměti. Pokud váš server není nastaven tak, aby s řetězcem dotazů udělal cokoliv, bude ignorován, ale název bude vypadat jako nový soubor v prohlížeči.

Na druhou stranu, pokud vyvíjíte webovou stránku, nechcete změnit číslo verze pokaždé, když uložíte změnu do své vývojové verze. To by bylo únavné.

Takže když vyvíjíte své stránky, dobrým trikem by bylo automatické generování parametru řetězce dotazu:

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

Přidání řetězce dotazu do požadavku je dobrým způsobem, jak verzi zdroje použít, ale pro jednoduchou webovou stránku to nemusí být nutné. A pamatujte si, že ukládání do mezipaměti je dobrá věc.

Za zmínku také stojí, že prohlížeč nemusí nutně strkat o uchovávání souborů v mezipaměti. Prohlížeče mají zásady pro tento druh věcí a obvykle hrají podle pravidel stanovených ve specifikaci HTTP. Pokud prohlížeč požádá server, je součástí odpovědi záhlaví EXPIRES .. datum, které informuje prohlížeč, jak dlouho by měl být uchováván v mezipaměti. Při příštím přechodu prohlížeče na požadavek na stejný soubor je vidět, že má kopii v mezipaměti a vypadá na datum EXPIRES, aby se rozhodlo, zda má být použit. 

Věřte tomu nebo ne, je to vlastně váš server, který dělá takovou mezipaměť prohlížeče tak vytrvalou. Můžete upravit nastavení serveru a změnit záhlaví EXPIRES, ale malá technika, kterou jsem napsal, je pro vás pravděpodobně mnohem jednodušší. Vzhledem k tomu, že ukládání do mezipaměti je dobré, obvykle chcete nastavit toto datum daleko do budoucna ("Dálkové záhlaví s vypršením budoucnosti") a vynutit změnu pomocí výše popsané techniky.

Máte-li zájem o více informací o protokolu HTTP nebo o tom, jak jsou tyto požadavky zpracovány, je dobrou knihou „Webové stránky s vysokým výkonem“ od společnosti Steve Souders. Je to velmi dobrý úvod k tématu.

176
keparo

Google mod_pagespeed plugin pro Apache pro vás provede automatickou verzi. Je to opravdu kluzké.

Analyzuje HTML na cestě z webového serveru (pracuje s PHP, Rails, python, statickým HTML - cokoliv) a přepisuje odkazy na CSS, JS, obrazové soubory tak, aby obsahovaly id kód. Slouží k souborům na upravených adresách URL s velmi dlouhým řízením mezipaměti. Když se soubory změní, automaticky změní adresy URL, aby je prohlížeč musel znovu načíst. V podstatě to funguje, bez jakýchkoliv změn kódu. Bude to dokonce i ztišit váš kód na cestě ven taky.

111
Leopd

Namísto změny verze ručně, doporučuji použít MD5 hash skutečného souboru CSS.

Takže vaše URL by bylo něco jako

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

Stále můžete použít pravidlo přepsání, aby se odstranil hash, ale výhodou je, že nyní můžete nastavit zásadu mezipaměti na „cache forever“, protože pokud je adresa URL stejná, znamená to, že soubor se nemění.

Potom můžete napsat jednoduchý skript Shell, který by vypočítal hash souboru a aktualizoval značku (pravděpodobně byste ji chtěli přesunout do samostatného souboru pro zahrnutí).

Jednoduše spusťte tento skript pokaždé, když se změní CSS a jste dobrý. Prohlížeč znovu načte soubory, když jsou změněny. Pokud provedete úpravy a poté je vrátíte zpět, není žádná bolest při určování, kterou verzi je třeba vrátit, aby se návštěvníci nemuseli znovu stahovat.

90
levik

Nejste si jisti, proč jste kluci berou tolik bolesti, že toto řešení implementujete.

Vše, co musíte udělat, když se soubor změnil timestamp a připojit jej jako querystring do souboru

V PHP bych to udělal jako:

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

filemtime je funkce PHP, která vrací soubor pozměněné časové razítko.

59
Phantom007

Na konci importu css/js můžete jednoduše umístit ?foo=1234 a změnit hodnotu 1234 na to, co se vám líbí. Prohlédněte si zdroj SO html.

Myšlenka, že je to? Parametry jsou v požadavku stejně ignorovány/ignorovány a toto číslo můžete změnit při zavádění nové verze.


Poznámka: Tam je nějaký argument s ohledem na přesně, jak to ovlivňuje ukládání do mezipaměti. Domnívám se, že obecné Gist je, že GET požadavky, s nebo bez parametrů by měly být ukládány do mezipaměti, takže výše uvedené řešení by mělo fungovat.

Nicméně, to je na obou webový server, aby se rozhodl, zda chce dodržovat tu část spec a prohlížeč, který uživatel používá, jak to může jít přímo před a požádat o novou verzi stejně.

51
SCdF

Slyšel jsem to nazvané "auto versioning". Nejběžnější metodou je zahrnutí mtime statického souboru někde do adresy URL a vyřazení pomocí popisovačů přepisů nebo adres URL:

Viz také:

38
John Millikin

Třicet stávajících odpovědí je skvělou radou pro webové stránky pro rok 2008. Pokud však jde o moderní, {single page application (SPA), může být na čase přemýšlet o některých základních předpokladech… konkrétně o myšlence, že je žádoucí, aby webový server sloužil pouze jedinému uživateli. , nejnovější verze souboru.

Představte si, že uživatel, který má verzi M SPA načten do vašeho prohlížeče:

  1. Vaše CD pipeline nasadí novou verzi N aplikace na server
  2. Navigujete v rámci SPA, která pošle XHR na server, aby získal /some.template
    • (Váš prohlížeč stránku neaktualizoval, takže stále běží verze M)
  3. Server odpoví obsahem /some.template - chcete vrátit verzi M nebo N šablony?

Pokud se formát /some.template změnil mezi verzemi M a N (nebo byl soubor přejmenován nebo cokoliv) pravděpodobně nebudete chtít verzi N z šablona odeslaná do prohlížeče, ve kterém je spuštěna stará verze M analyzátoru. †

Webové aplikace se do tohoto problému spouští, když jsou splněny dvě podmínky:

  • Zdroje jsou požadovány asynchronně někdy po úvodním načtení stránky
  • Logika aplikace předpokládá věci (které se mohou v budoucích verzích měnit) o ​​obsahu prostředků

Jakmile vaše aplikace potřebuje paralelně zobrazovat více verzí, řešení ukládání do mezipaměti a opětovné načtení se stane triviální:

  1. Nainstalujte všechny soubory webu do verzovaných souborů: /v<release_tag_1>/…files…, /v<release_tag_2>/…files…
  2. Nastavte hlavičky HTTP tak, aby umožnily prohlížečům ukládat soubory do mezipaměti
    • (Nebo ještě lépe, dát všechno do CDN)
  3. Aktualizujte všechny tagy <script> a <link> atd., Abyste ukázali na tento soubor v jednom z verzovaných verzí

Ten poslední krok zní složitě, protože by to mohlo vyžadovat volání tvůrce adresy URL pro každou adresu URL na straně serveru nebo na straně klienta. Nebo můžete jen chytře použít tag <base> a změnit aktuální verzi na jednom místě.

† Jedním ze způsobů, jak to udělat, je být agresivní v tom, že prohlížeč musí znovu načíst vše, když je vydána nová verze. Aby však bylo možné dokončit všechny probíhající operace, může být stále nejjednodušší podporovat paralelně alespoň dvě verze: v-current a v-previous.

22
Michael Kropat

Nepoužívejte foo.css? Version = 1! Prohlížeče nemají ukládat adresy URL do proměnných GET. Podle http://www.thinkvitamin.com/features/webapps/serving-javascript-fast , IE a Firefox to ignorují, Opera a Safari ne! Místo toho použijte foo.v1234.css a použijte pravidla pro přepsání, aby se číslo verze odstranilo.

14
airrob

Pro ASP.NET 4.5 a vyšší můžete použít svazkování skriptů .

Požadavek http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81 je pro svazek AllMyScripts a obsahuje pár řetězců dotazů v = r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81. Řetěz dotazu v má token hodnoty, který je jedinečným identifikátorem používaným pro ukládání do mezipaměti. Dokud se balíček nezmění, aplikace ASP.NET požádá o tento svazek balíček AllMyScripts. Změní-li se nějaký soubor ve svazku, optimalizační rámec ASP.NET vygeneruje nový token, který zaručí, že požadavky na prohlížeč pro tento balíček získají nejnovější balíček.

Existují i ​​další výhody spojené s balíčkem, včetně zvýšeného výkonu při prvním načítání stránky s minifikací.

10
user3738893

RewriteRule potřebuje malou aktualizaci pro soubory js nebo css, které obsahují tečkový zápis na konci. Např. json-1.3.js.

Přidal jsem tečku negace tečky [^.] K regexu tak .number. je ignorován.

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

Zde je čisté JavaScript řešení

(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);
    }

})();

Výše uvedené bude hledat naposledy, kdy uživatel navštívil vaše stránky. Pokud byla poslední návštěva před vydáním nového kódu, použije location.reload(true) k vynucení obnovení stránky ze serveru. 

Obvykle to mám jako první skript v rámci <head>, takže je vyhodnocen před jakýmkoliv jiným obsahem. Je-li třeba znovu načíst, je pro uživatele sotva patrný.

Používám místní úložiště pro uložení poslední časové značky návštěvy v prohlížeči, ale můžete přidat soubory cookie, pokud chcete podporovat starší verze IE.

8
Lloyd Banks

Zajímavý příspěvek. Po přečtení všech odpovědí zde v kombinaci se skutečností, že jsem nikdy neměl žádné problémy s "falešnými" řetězci dotazů (které si nejsem jistý, proč je každý tak ochotný použít) Myslím, že řešení (které odstraňuje potřebu pravidel pro přepis Apache) jako v přijaté odpovědi) je výpočet krátkého HASH obsahu souboru CSS (namísto souboru datetime) jako falešný dotazový řetězec.

Výsledkem by bylo následující:

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

Samozřejmě datetime řešení také získat práci v případě úpravy souboru CSS, ale myslím, že je to o obsahu souboru css, a ne o souboru datetime, tak proč si tyto smíšené?

8
Michiel

V Laravel (PHP) to můžeme provést následujícím jasným a elegantním způsobem (pomocí časového razítka modifikace souboru):

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

A podobné pro CSS

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

Díky Kipovi za jeho dokonalé řešení!

Rozšířil jsem ho tak, aby ho používal jako Zend_view_Helper. Protože můj klient spouští svou stránku na virtuálním hostiteli, rozšířil jsem ho také o to.

Doufám, že to pomůže i někomu jinému.

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

}

Na zdraví a díky.

6
lony

Nezjistil, že by klientský přístup DOM s klientem vytvořil dynamicky prvek skriptového uzlu (nebo 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>
5
GreQ

Můžete jednoduše přidat nějaké náhodné číslo s CSS/JS url jako

example.css?randomNo=Math.random()
5
Ponmudi VN

Řekněte, že máte k dispozici soubor na adrese:

/styles/screen.css

na URI můžete buď připojit parametr dotazu s informacemi o verzi, např .:

/styles/screen.css?v=1234

nebo můžete předplatit informace o verzi, např .:

/v/1234/styles/screen.css

IMHO druhá metoda je lepší pro soubory CSS, protože mohou odkazovat na obrazy s použitím relativních adres URL, což znamená, že pokud zadáte background-image jako takto:

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

jeho URL bude efektivně:

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

To znamená, že pokud aktualizujete číslo verze používané server bude považovat za nový prostředek a nepoužívejte v mezipaměti verze. Pokud založíte číslo své verze na Subversion/CVS/atd. revize znamená, že budou zaznamenány změny obrázků odkazovaných v souborech CSS. To není garantováno prvním schématem, tj. URL images/happy.gif vzhledem k /styles/screen.css?v=1235 je /styles/images/happy.gif, které neobsahuje žádné informace o verzi.

Implementoval jsem řešení pro ukládání do mezipaměti pomocí této techniky s Java servlety a jednoduše zpracovávám požadavky na /v/* s servletem, který deleguje na podkladový zdroj (tj. /styles/screen.css). V režimu vývoje nastavuji záhlaví pro ukládání do mezipaměti, které sdělují klientovi, aby vždy zkontroloval čerstvost zdroje se serverem (obvykle to má za následek 304, pokud delegujete na soubor DefaultServlet a .css, .js atd. Soubor Tomcat) a soubor se nezměnil). v režimu nasazení nastavuji záhlaví, které říkají "cache navždy".

5
Walter Rumsby

Pokud přidáte id session jako spureous parametr souboru js/css, můžete vynutit "ukládání do mezipaměti pro celou relaci":

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

Pokud chcete, aby se do mezipaměti verze nacházela celá verze, můžete přidat nějaký kód pro tisk data souboru nebo podobně. Pokud používáte Javu, můžete použít vlastní značku pro vytvoření odkazu elegantním způsobem.

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

Pro technologii ASP.NET předpokládám další řešení s pokročilými možnostmi (režim ladění/vydání, verze):

Soubory Js nebo Css zahrnuté tímto způsobem:

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

Global.JsPostfix a Global.CssPostfix se vypočte následujícím způsobem v 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();
    }
    ...
}
5
Ivan Kochurkin

google Chrome má Hard Reload stejně jako Empty Cache a Hard Reload option.You můžete kliknout a podržet tlačítko reload (In Inspect Mode) pro výběr.

4
unknown123

Nedávno jsem to vyřešil pomocí Pythonu. Zde by měl být kód (snadno použitelný v jiných jazycích):

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) 

Tento kód v zásadě připojí časové razítko soubory jako parametr dotazu k adrese URL. Volání následující funkce

script("/main.css")

bude mít za následek

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

Výhodou samozřejmě je, že už nikdy nebudete muset měnit svůj html, dotykem na soubor CSS se automaticky spustí zneplatnění mezipaměti. Funguje velmi dobře a režijní náklady nejsou patrné.

4
pi.

Pro můj vývoj jsem zjistil, že chrom má skvělé řešení.

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

S nástroji pro vývojáře otevřete, jednoduše klikněte na tlačítko Obnovit a pusťte, jakmile přejdete na položku „Prázdná vyrovnávací paměť a pevný soubor“.

To je můj nejlepší přítel, a je to super lehký způsob, jak dostat to, co chcete!

4
Frank Bryce

Přidávám tuto odpověď jako specifickou odpověď SilverStripe http://www.silverstripe.org , kterou jsem hledal a nikdy jsem nenašel, ale vycházel jsem z čtení: http://api.silverstripe.org /3.0/source-class-SS_Datetime.html#98-110

Doufejme, že to pomůže někomu, kdo používá šablonu SilverStripe a pokouší se vynutit načtení obrazu z mezipaměti na každé návštěvě/obnovení stránky. V mém případě se jedná o gif animaci, která hraje pouze jednou a proto se po jejím uložení do mezipaměti nepřehrála. V mé šabloně jsem jednoduše přidal:

?$Now.Format(dmYHis)

na konci cesty k souboru vytvořit jedinečné časové razítko a přinutit prohlížeč, aby s ním zacházel jako s novým souborem. 

2
pinkp

Omlouvám se za to, že jsem vrátil mrtvou nit. 

@ TomA má pravdu. 

Použití metody "querystring" nebude uloženo do mezipaměti podle citace Steve Souders níže:

... že Squid, oblíbený proxy server, nemá prostředky ke vyrovnávací paměti pomocí querystring.

@ TomA návrh použití style.TIMESTAMP.css je dobrý, ale MD5 by byl mnohem lepší, než kdyby se obsah skutečně změnil, změní se také MD5.

2
Tai Li

Zdá se, že všechny odpovědi zde naznačují nějakou verzi v systému pojmenování, který má své nevýhody.

Prohlížeče by si měly být dobře vědomy toho, co ukládat do mezipaměti a co ne ukládat do mezipaměti čtením odpovědi webového serveru, zejména hlavičky http - jak dlouho je tento zdroj platný? byl tento zdroj aktualizován od posledního načtení? atd. 

Pokud jsou věci nakonfigurovány správně, aktualizace souborů aplikace by měla v určitém okamžiku obnovit prohlížeče. Můžete například nakonfigurovat váš webový server tak, aby prohlížeč nemusel ukládat soubory do vyrovnávací paměti (což je špatný nápad). 

Podrobnější vysvětlení, jak to funguje, je zde https://www.mnot.net/cache_docs/#WORK

2
commonpike

Vložil jsem MD5 hash obsahu souboru do jeho URL. Tímto způsobem mohu nastavit velmi dlouhé datum vypršení platnosti a nemusím se starat o uživatele, kteří mají starý JS nebo CSS.

Také jsem to vypočítat jednou za soubor za běhu (nebo na změny systému souborů), takže není nic zábavného dělat v době návrhu nebo během procesu sestavení.

Pokud používáte ASP.NET MVC, můžete se podívat na kód v mé další odpovědi zde .

2
Drew Noakes

Chcete-li přidat datum souboru, stačí použít kód na straně serveru ... tímto způsobem bude uložen do mezipaměti a bude znovu načten pouze při změně souboru

V prostředí ASP.NET

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

<script type="text/javascript" src="~/js/[email protected](System.Text.RegularExpressions.Regex.Replace(File.GetLastWriteTime(Server.MapPath("~/js/custom.js")).ToString(),"[^0-9]", ""))"></script>    
2
Luis Cambustón

Doporučuji provést následující postup:

  • verze souborů css/js při každém nasazení, něco jako: screen.1233.css (číslo může být revizí SVN, pokud používáte systém pro správu verzí)

  • minimalizovat, aby se optimalizovaly doby načítání

2
Dan Burzo

V prostředí Java Servlet se můžete podívat na knihovnu Jawr . Stránka funkcí vysvětluje, jak zpracovává ukládání do mezipaměti:

Jawr se bude snažit přinutit své klienty, aby vyrovnaly zdroje. Pokud se prohlížeč zeptá, zda se soubor změnil, záhlaví 304 (nezměněné) se odešle zpět bez obsahu. Na druhou stranu, s Jawr budete 100% jisti, že nové verze balíčků jsou staženy všemi klienty. Každá adresa URL vašich zdrojů bude obsahovat automaticky generovanou předponu založenou na obsahu, která se automaticky změní při každé aktualizaci vzoru. Jakmile nasadíte novou verzi, změní se i adresa URL balíku, takže nebude možné, aby klient používal starší verzi uloženou v mezipaměti.

Knihovna také provádí zcizení js/css, ale můžete to vypnout, pokud nechcete.

2
holmis83

Pokud používáte git + php, můžete skript znovu načíst z mezipaměti pokaždé, když dojde ke změně repo programu git pomocí následujícího kódu:

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

Jednoduše přidejte tento kód, kam chcete provést těžké znovu načíst (vynutit, aby prohlížeč znovu načetl soubory CSS/JS uložené v mezipaměti) Udělejte to uvnitř souboru .load, aby se neobnovoval jako smyčka

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

Pokud používáte moderní prohlížeč, můžete použít soubor manifestu k informování prohlížečů, které soubory je třeba aktualizovat. To nevyžaduje žádné záhlaví, žádné verze v adresách URL atd.

Další podrobnosti viz: Viz: https://developer.mozilla.org/nl/docs/Web/HTML/Applicatie_cache_gebruiken#Introduction

2
Jos

Vidím problém s přístupem k použití timestamp- nebo hash-založené diferenciace v URL zdroje, který dostane svlékl na požádání na serveru. Stránka, která obsahuje odkaz na např. list stylů může být také uložen do mezipaměti. Stránka uložená v mezipaměti tak může vyžadovat starší verzi šablony stylů, ale bude doručena nejnovější verzi, která může nebo nemusí fungovat s požadovanou stránkou.

Chcete-li tento problém vyřešit, musíte buď chránit stránku s požadavkem pomocí záhlaví no-cache nebo meta, abyste se ujistili, že se obnoví při každém zatížení. Nebo musíte zachovat všechny verze souboru stylů, který jste kdy nasadili na server, každý jako samostatný soubor a jejich odlišovač neporušený, takže požadující stránka se může dostat k verzi souboru stylu, ve kterém byla. navržený pro. V druhém případě se v podstatě spojíte s verzí stránky HTML a šablony stylů, což lze provést staticky a nevyžaduje žádnou serverovou logiku.

1
ThomasH

"Další myšlenkou, kterou navrhl SCdF, by bylo přidat do souboru falešný řetězec dotazů. (Některý kód Pythonu automaticky používá časové razítko jako falešný řetězec dotazů byl odeslán pi.) Nicméně existuje nějaká diskuse o tom, zda nebo ne. ne prohlížeč by mezipaměť soubor s řetězcem dotazu. (Pamatujte, že chceme, aby prohlížeč soubor do mezipaměti a použít ho při budoucích návštěvách. Chceme, aby soubor znovu načíst, když se změnil.) Vzhledem k tomu, že to není jasné co se stane s falešným řetězcem dotazů, tuto odpověď nepřijímám. “

<link rel = "stylesheet" href = "file.css? <? = hash_hmac ('sha1', session_id (), md5_file (" file.css "));?>" />

Hashing soubor znamená, že když se změnil, řetězec dotazu se změní. Pokud tomu tak není, zůstane to stejné. Každá relace také nutí znovu načíst.

Volitelně můžete také pomocí přepisů způsobit, že prohlížeč bude považovat za nový identifikátor URI

1

Zakázat cache skript.js pouze pro lokální vývoj v čistém JS

injektuje náhodný script.js? wizardry = 1231234 a blokuje pravidelné script.js

<script type="text/javascript">
  if(document.location.href.indexOf('localhost') !== -1) {
    const scr = document.createElement('script');
    document.setAttribute('type', 'text/javascript');
    document.setAttribute('src', 'scripts.js' + '?wizardry=' + Math.random());
    document.head.appendChild(scr);
    document.write('<script type="application/x-suppress">'); // prevent next script(from other SO answer)
  }
</script>
<script type="text/javascript" src="scripts.js">
1
Pawel

Další návrh pro webové stránky ASP.Net,

  1. Nastavte různé mezipaměti: maximální věkové hodnoty pro různé statické soubory. 
  2. U souborů css/js je pravděpodobnost změny těchto souborů na serveru vysoká, takže nastavte minimální kontrolu mezipaměti: maximální věková hodnota 1 nebo 2 minuty nebo něco, co vyhovuje vašim potřebám. 
  3. V případě obrázků nastavte jako datové úložiště vzdálenou hodnotu: max. Věková hodnota, například 360 dní. 
  4. Když provedeme první požadavek, veškerý statický obsah se stáhne do klientského počítače s odpovědí 200 OK.
  5. V následujících požadavcích a po dvou minutách vidíme požadavky 304-Not Modified na soubory css a js, které nám zabraňují verzím css/js.
  6. Obrazové soubory nebudou požadovány, protože budou použity z mezipaměti paměti až do vypršení platnosti mezipaměti.
  7. Použitím níže uvedených konfigurací web.config můžeme dosáhnout výše popsaného chování, . ] [.].] . ]

Přišel jsem k této otázce, když jsem hledal řešení pro mé SPA, které má pouze jeden index.html se seznamem všech potřebných souborů. I když jsem dostal nějaké tipy, které mi pomohly, nemohl jsem najít rychlé a snadné řešení.

Nakonec jsem napsal rychlou stránku (včetně celého kódu), která je nezbytná pro autoversion html/js index.html jako součást procesu publikování. Funguje perfektně a aktualizuje pouze nové soubory na základě data naposledy upraveného. 

Můj příspěvek můžete vidět na http://blueskycont.com/wp/2016/05/12/autoversion-your-spa-index-html/ . Tam je také volný pracovní winapp tam.

Vnitřek kódu je

       private void ParseIndex(string inFile, string addPath, string outFile)
    {
        string path = Path.GetDirectoryName(inFile);
        HtmlAgilityPack.HtmlDocument document = new HtmlAgilityPack.HtmlDocument();
        document.Load(inFile);
        foreach (HtmlNode link in document.DocumentNode.Descendants("script"))
        {
            if (link.Attributes["src"]!=null)
            {
                resetQueryString(path, addPath, link, "src");
            }
        }
        foreach (HtmlNode link in document.DocumentNode.Descendants("link"))
        {
            if (link.Attributes["href"] != null && link.Attributes["type"] != null)
            {
                if (link.Attributes["type"].Value == "text/css" || link.Attributes["type"].Value == "text/html")
                {
                    resetQueryString(path, addPath, link, "href");
                }
            }
        }
        document.Save(outFile);
        MessageBox.Show("Your file has been processed.", "Autoversion complete");
    }

    private void resetQueryString(string path, string addPath, HtmlNode link, string attrType)
    {
        string currFileName = link.Attributes[attrType].Value;

        string uripath = currFileName;
        if (currFileName.Contains('?')) uripath = currFileName.Substring(0, currFileName.IndexOf('?'));
        string baseFile = Path.Combine(path, uripath);
        if (!File.Exists(baseFile)) baseFile = Path.Combine(addPath, uripath);
        if (!File.Exists(baseFile)) return;
        DateTime lastModified = System.IO.File.GetLastWriteTime(baseFile);
        link.Attributes[attrType].Value = uripath + "?v=" + lastModified.ToString("yyyyMMddhhmm");
    }
1
statler

Mnoho odpovědí zde obhajuje přidání časové značky do url. Pokud neupravujete své výrobní soubory přímo, časové razítko souboru pravděpodobně neodráží čas, kdy byl soubor změněn. Ve většině případů to způsobí změnu adresy URL častěji než samotný soubor. To je důvod, proč byste měli použít rychlý hash obsahu souboru, jako je MD5 jako levik a další navrhli.

Mějte na paměti, že hodnota by měla být vypočtena jednou při sestavení nebo spuštění, nikoli při každém požadovaném souboru.

Jako příklad zde uvádíme jednoduchý bash skript, který čte seznam souborů ze stdin a zapisuje soubor json obsahující hashes do stdout:

#!/bin/bash
# create a json map from filenames to md5s
# run as hashes.sh < inputfile.list > outputfile.json

echo "{"
delim=""
while read l; do
    echo "$delim\"$l\": \"`md5 -q $l`\""
    delim=","
done
echo "}"

Tento soubor by pak mohl být načten při spuštění serveru a odkazován namísto čtení souborového systému.

1
undefined

Jedním z nejlepších a nejrychlejších přístupů, které znám, je změna názvu složky, ve které máte Soubory CSS nebo JS. OR pro vývojáře. Změňte název souborů CSS/js, které mají podobu verzí.

<link rel="stylesheet" href="cssfolder/somecssfile-ver-1.css"/>

Udělejte to samé pro vaše soubory js.

0
Bangash

Moje metoda, jak to udělat, je jednoduše mít prvek odkazu na straně serveru:

<!--#include virtual="/includes/css-element.txt"-->

kde je obsah css-element.txt

<link rel="stylesheet" href="mycss.css"/>

takže den, kdy chcete odkazovat na my-new-css.css nebo cokoliv jiného, ​​stačí změnit zahrnutí.

0
AmbroseChapel

Malé zlepšení ze stávajících odpovědí ...

Použití náhodného čísla nebo ID relace by mělo za následek znovu načíst na každém požadavku. V ideálním případě budeme muset změnit pouze v případě, že některé změny kódu byly provedeny v libovolném souboru js/css. Při použití společného souboru JSP jako šablony pro mnoho dalších souborů jsp a js Přidejte níže do společného souboru JSP

<%@ taglib prefix="c" uri="http://Java.Sun.com/jsp/jstl/core"%>
<c:set var = "version" scope = "application" value = "1.0.0" />

Nyní použijte výše uvedenou proměnnou ve všech umístěních, jak je uvedeno níže ve vašem inkluzi souborů js.

<script src='<spring:url value="/js/myChangedFile.js?version=${version}"/>'></script>

Výhody:

1) Tento přístup vám pomůže při změně čísla verze pouze na jednom místě.

2) Udržení správného čísla verze (obvykle číslo sestavení/vydání) vám pomůže zkontrolovat/ověřit, zda jsou změny kódu správně nasazeny (z vývojářské konzoly prohlížeče).

Další užitečný tip:

Pokud používáte prohlížeč Chrome, můžete zakázat ukládání do mezipaměti, když je otevřen Dev Tool V prohlížeči Chrome Hit F12> F1 přejděte na Nastavení> Předvolby> Síť> Zakázat ukládání do mezipaměti (když je otevřen nástroj DevTools)

 Chrome DevTools

0

No, já jsem, aby to fungovalo mou cestou změnou verze js pokaždé, když se načtení stránky přidáním náhodného čísla do verze souboru js následujícím způsobem:

// Add it to the top of the page
<?php
srand();
$random_number = Rand();
?>

Poté použijte náhodné číslo na verzi js takto:

<script src="file.js?version=<?php echo $random_number;?>"></script>
0
Al3abMizo Games