RSS: Vše - Programování



Zrychlení načítání javascriptů

04. 02. 2008, 22:19:52,Programování,

Nová generace webových aplikací bohatě využívá javascriptu i kaskádových stylů k tomu, aby zvyšovala uživatelský komfort pro návštěvníky. Asynchronní dotazy na pozadí (AJAX), dynamické přepínání vzhledu, okamžitá odpověď aplikace bez nutnosti stránku odesílat zpět na server - to všechno se dnes stalo standardem. Tyto změny ovšem přinášejí i celou řadu komplikací. Jednou z nich je i nutnost kromě stránky samotné stahovat i větší množství doprovodného javascriptu, což výrazně prodlužuje celokovou dobu načtení. Problém lze částečně řešit cachováním - ale narážíme zde na to, že soubory s kódem pro klientský prohlížeč nejsou (jako například obrázky) typicky statickým obsahem. Protože se často jedná vlastně o plnohodnotné skripty s aplikační logikou, probíhají v nich poměrně často změny a my zkrátka v takovýchto okamžicích potřebujeme donutit klientský počítač stáhnout novou, aktualizovanou verzi. Jak tyto problémy vyřešit se pokusím naznačit v několika následujících odstavcích.

Spojování

Velice rozšířeným názorem je, že soubory s kaskádovými styly nebo s javascriptem bychom měli pokud možno spojovat do menšího množství větších monolitických souborů. Že je lepší mít jeden 50kB soubor, než deset souborů o velikosti 5kB. Omezíme tím počet požadavků na server a stránka se tak rychleji zobrazí. Důležité je si také uvědomit fakt, že jak Internet Explorer, tak i Firefox, dokáží najednou paralelně stahovat z jedné domény maximálně dva zdroje. V praxi to znamená, že zatímco prohlížeč pracně stahuje několik javascriptových souborů, na obrázky se nedostane a stránka se poměrně dlouhu dobu zobrazuje bez nich.

Tento přístup má samozřejmě i nevýhody. Zřejmě tou největší je ten fakt, že při každé, byť drobné změně javacriptu (resp. CSS), je potřeba dostat k uživateli kompletní (třeba 100kB) soubor dat.

Rozdělování

Za rozumný kompromis považuji ten případ, kdy je potřebný kód sice rozdělen, ale pouze do několika málo souborů. V každém z těchto souborů by se měly nacházet objekty (funkce, styly, ...), které spolu určitým způsobem souvisejí. V ideálním případě pak dosáhneme toho, že nebude třeba vždy nahrávat všechny tyto soubory, ale v závislosti na tom, kde se na stránkách nacházíme, budou nahrány vždy jen ty potřebné. Pokud těchto "knihoven" nebude příliš, nebudou vyžadovat ani příliš požadavků na webový server. Zároveň tímto způsobem výrazně omezíme negativní vliv monolitického přístupu, o kterém jsem psal v předchozím odstavci.

Komprese

Další oblíbená metoda, kterou se dá snížit čas potřebný ke stažení souborů s kaskádovými styly a javascriptem, je komprese. Zřejmě nečastější je nasazení mod_gzip. Tento modul serveru Apache při přijetí požadavku zkomprimuje obsah dokumentu (browser si ho po přijetí samozřejmě opět rozbalí), a tím zmenší jeho velikost. Pochopitelně se tak děje na úkor strojového času procesoru na serveru (komprese) i u klienta (dekomprese). Mod_gzip navíc při práci vytváří na disku dočasné soubory s komprimovanými daty, které nabídne ke stažení a následně smaže. A zde je další potenciální Achillova pata tohoto modulu - práce s diskem je mnohonásobně pomalejší než práce s pamětí a u velmi vytížených webových serverů se počet I/O operací může lehce dostat za hranici, kdy mod_gzip přestane přínášet zrychlení, ale naopak se stane brzdou.

Pro server Apache2 je k dispozici modul mod_deflate, který pracuje přímo s RAM pamětí, pro uživatele pracující s jedničkovou verzí Apache se nabízí možnost využít vytvořit virtuální disk v RAM paměti a mod_gzip pustit na něm. Také existuje možnost uložit statické předpřipravené komprimované verze souborů. Zdálo by se, že problém je tedy vyřešen. Jenže není.

Prohlížeče, které umožňují webovému serveru použít kompresi, tuto svou schopnost deklarují v hlavičce požadavku (např. "Accept-Encoding: gzip,deflate"). Bohužel některé verze Netscape 4 o sobě tvrdí, že kompresi zvládají, aniž by to byla pravda. Internet Explorer od verze 4 do verze 5 zase mívá problémy s dekompresí javasciptových souborů a může se stát, že rozbalí a ve stránce uplatní třeba jen polovinu celkového kódu. Zkrátka s kompresí na úrovní webového serveru to není jednoduché.

Vše se dá řešit ještě jinak - snížit velikost souborů beze změny jejich formátu. V aplikaci je pak potřebné udržovat dvě verze javascriptových a CSS souborů - komprimovanou a nekomprimovanou. Existuje celá řada nástrojů, které vám tuto kompresi ulehčí (např. DoJo compressor).

Cache

Cachování je náš nejlepší přítel. Dokonce i v těch případech, kdy se vlastní obsah stránek mění natolik dynamicky, že nemá cenu ho cachovat, bývají soubory s javascriptem a kaskádovými styly výbornými adepty na výjimku. Takže na otázku, zda cache používat, či nikoliv, je odpověď jasná. Problém ovšem je, na jak dlouho bychom měli nastavit její platnost. Den? Týden? Měsíc? Ideální řešení je samozřejmě navždy. Jenže jak pak zajistit stažení aktualizované verze souboru v případě změn či oprav?

K řešení tohoto problému využijeme mod_rewrite, o kterém jsem psal již dříve. Jednoduché pravidlo

RewriteRule ^/(.*\.)v[0-9.]+\.(css|js|gif|png|jpg)$	/$1$2	[L]

způsobí, že všechny požadavky na soubory s označením verze budou interně přesměrovány na soubory bez tohoto označení. Několik příkladů:

URL			          Přeloženo na
/images/foo.v2.gif -> /images/foo.gif
/css/main.v1.27.css -> /css/main.css
/javascript/md5.v6.js -> /javascript/md5.js

A jak nám to pomůže? Velmi! Na disku bude například stále fyzicky přítomen pouze soubor main.css. My na něj v hlavičce svých dokumentů budeme odkazovat jako na soubor main.v1.0.css a jako takový bude cachován s maximální možnou dobou platnosti. A ve chvíli, kdy v souboru uděláme jakoukoliv změnu, stačí zvýšit verzi v hlavičce dokumentu například na main.v1.1.css. Prohlížeč bude tento soubor považovat za jiný, než byl ten původní, stáhne ho a použije.

Možná vás napadne, že by stačilo používat něco ve tvaru /css/main.css?v=4. Jenže každý prohlížeč si již tradičně vykládá HTTP specifikaci po svém. IE a Firefox tedy cachovat URL s parametry umožňují, Opera a Safari ne. V konečném výsledku je proto tento přístup nešikovný.

 

Jedná se o zkrácený a volný překlad článku Serving javascript fast od Cala Hendersona, doplněný vlastními názory a postřehy.

Autor: Pavel Šindelka

Komentáře


[1] (VS) - 12. 02. 2008, 16:37:14
Dík za obeznámení s deflate a s tím necachováním u Opery a Safari u url s parametrem.

Nejlepší řešení mi přijde teď ten mod_rewrite a spojit to s generováním timestampu do jména souboru nějakým helperem při generování js/css tagu do šablony.
[2] (Jakub Vrána) - 12. 02. 2008, 16:38:58
Mohl bys uvést, kde je napsáno, že se nesmí cachovat URL s otazníkem? V HTTP specifikaci (RFC 2616) je napsáno pouze to, že URL s otazníkem by se nemělo cachovat v případě, že server nepošle hlavičky s expirací (sekce 13.9).

Nicméně Opera bohužel opravdu URL s parametrem necachuje.
[3] (Pavel Šindelka) - 12. 02. 2008, 19:34:30
[2] Díky za postřeh, dodnes jsem žil v přesvědčení, že tomu tak skutečně je. Nicméně přečetl jsem si inkriminovanou část specifikace znovu a musím Ti dát zapravdu.

Příslušný odstavec článku jsem upravil tak, aby neuváděl lidi v mýlku.
[4] (Michal Aichinger) - 17. 02. 2008, 00:40:27
Pavle dovolil jsem si trochu polemizovat.
[5] (Michal Aichinger) - 17. 02. 2008, 00:41:45
A tak ti ten system nejak orezava delku url, takze to postnu sem: http://www.czechdesign.cz/blogs/aichi/zrychleni-nacitani-javascript-a-css
[6] (Pavel Šindelka) - 17. 02. 2008, 11:15:56
Díky Michale za hezké doplnění, konkrétní metody spojování souborů či posílání hlaviček s expirací jsem (záměrně) nerozebíral - a je dobře, že tyhle informace budou k nalezení.

S vším, cos napsal, souhlasím - až na jednu výjimku. Použití mod_rewite skutečně podle mého smysl nepostrádá. Zmiňovaná zvýšená zátěž serveru způsobená tímto jedním rewritem navíc je, troufám si tvrdit, prakticky nezměřitelně malá a zanedbatelná. V zápisu "/js/main.js?13" nevidím nic jednoduššího oproti zápisu "/js/main.v13.js". A naopak stoprocentní funkčnost přes všechny prohlížeče (mimochodem podle mých informací (http://technet.idnes.cz/pro...) má u nás Opera kolem 5% a technické komunitě kolem 15% podílu na trhu) mi přijde jako výrazné plus. V tomto případě se mi zkrátka zdá mnou prezentované řešení o fous lepší.

Nicméně jsem moc rád, že ses něco konečně rozhodl sepsat. Moc rád (a určitě v tom nebudu sám) si přečtu Tvoje postřehy z PHP a hlavně Javascriptu. Tam se mám ještě hodně co učit :)

BTW: Rád bych ten komentář napsal přímo na Tvůj blog, ale odesílání i náhledy komentářů končí chybou při volání funkce check_perm()

Komentovat


Tento článek byl uzavřen. Už není možné k němu přidávat komentáře ani hlasovat