Možná se vám také již stalo, že jste na svůj web nahráli nový obrázek, upravený CSS vzhled nebo opravili nějakou JS chybu, ale uživatelům se tato změna neprojevila (nebo se projevila jen částečně).
To je způsobeno (paradoxně) tím, že máte správně nastavený server, který umožňuje prohlížeči cachovat soubory (obrázky, JS, CSS, písma, atd.) a zrychlovat tím načítání. Negativní důsledek toho je pak právě to, že pokud se jen změní obsah souboru, prohlížeč to nepozná a stále používá starou verzi, kterou má uloženou.
Existuje několik způsobů, ale všechny jsou založeny na stejném principu – musíte změnit jméno souboru a tím donutit prohlížeč ho znovu stáhnout. Nejde přitom ani tak o to, aby se změnilo skutečně jméno souboru, ale aby si prohlížeč myslel, že se změnilo (resp. prohlížeč si pak myslí, že jde o zcela nový souboru – změnu jména nijak nedetekuje).
Verzování ve jméně
Tahle metoda není příliš šikovná, protože po každé změně musíte skutečně měnit jméno souboru a hlavně měnit všechny odkazy tam, kde se používají. Sice, pokud máte web správně navržen, může být tato změna jen na jednom místě, ale stále je to otrava a navíc je to náchylné k chybám (zapomenete něco změnit, překlepnete se, apod.)
Tato metoda se používá u knihoven jako je jQuery:
<script type="application/javascript" src="/js/jquery-1.11.1.min.js">
Když pak stáhnete do webu novou verzi jQuery, musíte přepsat všechny tyto adresy. A pokud někdo z jQuery komunity místo pomlčky napíše do jména tečku (jquery.1.11.1.min.js) či podtržítko (jquery_1.11.1.min.js) a vy si toho nevšimnete, můžete si v klidu jednou změnou umrtvit celý web. Proto je lepší mít jméno souboru vždy stejné (např. jquery.latest.min.js) a verzi měnit jinými způsoby (viz dále).
Verzování v parametru
Opakem změny jména souboru je změna jeho parametru. Jak jistě víte, každá HTTP adresa se skládá z několika částí (adresa serveru, cesta k souboru, parametry a kotva). Adresu serveru měnit nemůžete, změnu jména souboru jsme si ukázali jako nevhodnout a kotva se při stahování souboru vůbec neuvažuje.
Jediné, co tedy zbývá, je změna parametru. A jako nejvhodnější se ukazuje přidání nějakého (relativně) náhodného čísla nebo řetězce:
<script src="/js/jquery.min.js?v=1.11.1"> <img src="/images/logo.jpg?v=2">
Většina serverů (Apache, IIS, apod.) ignoruje parametry pro soubory, které se stahují, takže nijak nezmění způsob, jakým se soubor stahuje. Jediný rozdíl je v tom, že prohlížeč považuje parametry za součást jména a tak je nucen stáhnout soubor znovu (i když je to třeba ten samý soubor):
Stáhne jeden obrázek dvakrát: <img src="/images/logo.jpg?v=2"> <img src="/images/logo.jpg?v=3">
Zpravidla se pro verzování používá parametr „v=“ nebo (pro menší čitelnost) „_=“. Použít ale lze i způsob bez pojmenovaného parametru (viz dále).
Jako číslo verze lze použít skutečně verzi (např. u obrázku 1, 2, 3 atd. nebo v případě sdílené knihovny její skutečnou verzi, např. 1.11.1) nebo nějaké číslo či hash, které zabrání uživatelům (a hlavně hackerům) aby jednoduše zjistili aktuální verzi souborů.
Jako číslo verze lze použít třeba datum poslední změny souboru (ve formátu timestamp, base64, apod.), nějaký hash souboru (CRC32, MD5, SHA1, apod.) nebo dokonce zcela náhodné číslo (např. GUID). Zde hlavně záleží na tom, jak budete číslo verze měnit – pokud ručně, záleží na tom, co je pro vás nejschůdnější, pokud automaticky, záleží hlavně na tom, zda potřebujete spíše rychlost nebo přesnost (a také k jakým údajům jste schopni se dostat z automatizačního skriptu).
Obrázek s timestamp poslední změny: <img src="/images/logo.jpg?v=1234567890"> Obrázek s CRC32 jeho obsahu: <img src="/images/logo.jpg?_=a6f84e63">
Pokud se nemůžete rozhodnout, jak verzovací parametr pojmenovat, není potřeba ani jméno zadávat a uvést jen číslo verze:
Obrázek s timestamp bez pojmenování: <img src="/images/logo.jpg?1234567890"> Skript s parametry: <img src="/images/generate.php?file=logo&format=jpg&size=800x600&1234567890">
Akorát pozor u skriptů, kterým by to mohlo zkomplikovat život. Pokud (jako výše uvedený) používá pouze vyjmenované parametry (file, format, size), další nepojmenovaný parametr by mu neměl vadit. Pokud ale načítá parametry v nějakém cyklu a zpracovává je, pak by to mohlo mít neblahý účinek a verzovací číslo se může objevit někde v textu či DB nebo dokonce může způsobit chybu, pokud skript kontroluje jména parametrů proti nějakému slovníku (např. jména parametrů odpovídají jménům sloupců v DB a skript pak vyhodí „Nenalezen sloupec 123456789!“).
Verzování při buildu
Pokud máte web tzv. „builděný“ (tzn. před jeho nahráním na web ho zpracujete nějakými skripty, které např. vyhazují komentáře, minifikují a/nebo spojují soubory, atd.), můžete přidat ještě verzování souborů, které automaticky ve všech souborech nahradí odkazy na ostatní soubory tak, aby každý prohlížeč byl nucen po nahrání nové verze na web ji stáhnout.
V tomto případě je nejlepší jako číslo verze používat jednotné číslo nebo řetězec, který bude unikátní pro číslo buildu. V souborech pak můžete používat konstantu místo verze, kterou pak skript nahradí:
<img src="/images/logo1.jpg?%BUILD_VERSION%"> <img src="/images/logo2.jpg?%BUILD_VERSION%"> //po buildu se nahradí za: <img src="/images/logo1.jpg?1234567890> <img src="/images/logo2.jpg?1234567890>
Nevýhoda toho přístupu je v tom, že po updatu webu musí prohlížeče stáhnout znovu všechny soubory včetně těch, které se v nové verzi nezměnili.
Samozřejmě můžete udělat buildění chytřejší a po nalezení klíčového slova přečíst celé jméno souboru, najít ho na disku a vypočítat jeho vlastní verzi. Zde jako verzi můžete použít přímo hash obsahu souboru, protože buildy se neprovádí tak často a zpravidla nevadí, že bude trvat trochu déle.
Naopak pokud máte na webu velké soubory (např. PDF, které může mít i desítky MB), u kterých by výpočet trval dlouho, nebo pokud pro testování provádíte „hodinové“ buildy (a build tedy musí trvat co nejkratší dobu), pak je lepší místo obsahu pracovat jen s datem změny.
Verzování za běhu
Pokud svůj web updatujete jednoduše tím, že změníte jednotlivé soubory (přes FTP, SSH, apod.), je nejlepší metoda měnit čísla verzí za běhu. To ale vyžaduje, aby soubory zpracovával nějaký skript (např. PHP).
Pak můžete jména souborů vkládat do HTML či jiných souborů pomocí funkce, která vždy před vygenerováním přečte datum změny souboru a přidá ho do jeho adresy:
Vždy aktuální obrázek: <img src="<?php echo getVersion('/images/logo.jpg'); ?>"> <?php function getVersion(file) { $filename = realpath(PUBLIC_DIR . file); return file . '?' . filemtime($filename); }
V tomto případě není vhodné zpracovávat hash obsahu souboru, protože každý takto vložený soubor zdržuje generování výsledné HTML stránky.
Pro verzování souborů v CSS (obrázky, písma, apod.) bude potřeba napsat skript, který daný CSS soubor načte, najde v něm definice „url()“ a nahradí jména uvnitř závorek.
$css = file_get_contents($_GET['file']); $urlRX = '/url\(([\'\"]?' . '([^\'\"\)]+)[\'\"]?)\)/im'; preg_match_all($urlRX, $css, $matches); foreach ($matches as $match) { $version = getVersion($match[2]); $version = $match[2] . '?' . $version; str_replace($css, $match[1], $version); } die($css); //return content of the file
CSS soubory pak budete vkládat takto (kód verze přísluší danému CSS souboru):
<link href="/css.php?file=/css/theme.css&123456" type="text/css" rel="stylesheet" media="screen" >
Pro verzování v LESS souborech můžete použít můj plugin less-plugin-version:
<span class="pl-e">.logo</span> { <span class="pl-c1">background-image</span>: versioned(<span class="pl-s"><span class="pl-pds">'</span>/img/logo.jpg<span class="pl-pds">'</span></span>); }