3 praktické triky pro rychlejší načítání webu

Rychlost je dnes klíčem k úspěchu a na webu to platí dvojnásob. Představím vám proto 3 efektivní a praktické triky profesionálů pro rychlejší načítání webu pomocí optimalizace CSS, JavaScriptu a komprese dat.

Pokud se blíže podíváte na síťovou komunikaci mezi prohlížečem a serverem, zjistíte, že načtení jedné stránky představuje ve skutečnosti hned celou řadu samostatných spojení. Kromě HTML požádá prohlížeč server také o:

  • kaskádové styly (CSS),
  • JavaScripty,
  • obrázky a
  • případně i webové fonty.

Každý takový dotaz a přijetí odpovědi zabere několik desítek až stovek milisekund. Nejlepší způsob jak tento drahocenný čas ušetřit je redukovat počet těchto spojení na minimum. Toho můžeme dosáhnout sloučením a využitím cache.

Kaskádové styly

Kodéři CSS často bojují s rozporuplnými potřebami svých zákazníků. Na jedné straně musí být schopni UI rychle upravit, na straně druhé je od nich očekáváno rychlejší načítání webu. Ačkoliv by tedy po vzoru jiných programátorů rádi měli oddělené a znovu použitelné kusy CSS kódu, často nakonec rezignují a skončí u jednoho obrovského CSS souboru se stovkami řádků.

Ideálním řešením tohoto problému jsou dnes populární CSS preprocesory LESS či námi oblíbený SASS. Kromě separace znovupoužitelných komponent, finálního sloučení a kompresi, vám tyto preprocesory přinášejí i další přednosti skriptovacích jazyků, jako jsou funkce, proměnné či řídící struktury.

Slíbil jsem praktická a efektivní řešení pro rychlejší načítání webu. Pokud tedy nemáte čas na LESS/SASS, slučte ve správném pořadí CSS soubory (včetně CSS souborů případných JQuery pluginů).

Windows:

copy file1.css+file2.css+file3.css project-all.css

UN*X:

cat file1.css file2.css file3.css > project-all.css

K minimalizaci pak použijte Yahoo YUI Compressor. Jedná se o Java knihovnu, kterou si můžete stáhnout na stránkách Yahoo pro developery a použít ji na své příkazové řádce:

java -jar yuicompressor-x.y.z.jar -type css project.css -o project.min.css

Jako super rychlou cestu můžete použít některé webové rozhraní pro práci s touto knihovnou – např.: http://refresh-sf.com/yui/

První vítězství je naše – máme soubor (např. project.min.css), který obsahuje sloučené a komprimované kaskádové styly našeho webu. V naší HTML hlavičce tak linkujeme jen jediný CSS soubor:

<html>
  <head>
 
    …
 
    <link href="/css/project.min.css" rel="stylesheet" type="text/css">
  </head>
  <body>
 
    …
 

  </body>
</html>

JavaScript

Světu JavaScriptu dnes vládne jQuery. Pluginy typicky představují jednotlivé JavaScript soubory a k nim případně přípojené další přílohy – CSS, obrázky, flash komponenty apod. Pro stažení všech pluginů je pak často třeba mnoho spojení, což příliš nepodporuje rychlejší načítání webu. Pro sloučení a kompresi JavaScriptu můžete využít již výše uvedený YUI Compressor, nebo námi oblíbenou alternativu od Google nazývanou Closure Compiler.

Stejně jako YUI Compressor je i Google Closure Compiler Java knihovna, kterou můžete využívat na příkazové řádce.

java -jar compiler.jar --js plugin1.js --js plugin2.js --js project.js --js_output_file project.min.js

Snadno použitelné webové rozhraní Closure Compileru naleznete na stránce: http://closure-compiler.appspot.com/home. V levé části obrazovky můžete přímo ze svého stávajícího webu přidat jednotlivé JavaScript soubory a v pravé straně obrazovky vám pak Compiler navrátí kompilovaný kód včetně případných chyb a varování.

Kromě vynechání nepotřebných znaků provádějí JavaScript kompilátory také další optimalizace pro rychlejší načítání webu. Výsledný kód tak sice není moc čtivý, ale o to efektivnější je pro prohlížeče.

Protože se JavaScript zpravidla interpretuje až po načtení celé stránky, je vhodnější umístit načtení našeho minifikovaného souboru místo do HTML hlavičky těsně před koncovou značku </body>:

<html>
  <head>
 
    …
 
    <link href="/css/project.min.css" rel="stylesheet" type="text/css">
  </head>
  <body>
 
    …
 
    <script src="/js/project.min.js"></script>
  </body>
</html>

A máme druhé vítězství – jediný soubor (např. project.min.js), který obsahuje zkompilované všechny JavaScripty našeho webu. Před koncovou značku </body> tak linkujeme jen jediný JavaScript soubor.

Ovládnutí cache

Již nyní je vaší odměnou úspora několika stovek milisekund, za které vás pochválí vyhledávače i návštěvníci webu. Ještě pořád však máme v rukávu druhou strategii, jak minimalizovat počet spojení se serverem – a tou je Cache.

Cache je v IT velmi obecný pojem. Všeobecně má cache zajistit, že často využívané zdroje, které jsou těžko dostupné, jsou uloženy do lépe dostupné paměti. Tato lépe dostupná paměť je pak typicky omezená a drahá, a proto je třeba pečlivě vybírat, co bude do cache uloženo.

Získání zdrojů jako jsou obrázky, CSS či JavaScript jsou pro webový prohlížeč časově náročné operace, které znesnadňují rychlejší načítání webu. Proto implementují prakticky všechny prohlížeče interní cache mechanizmus. Výhodou cache prohlížeče je fakt, že má obrovskou kapacitu. Lokální HDD bude vždy rychlejší než webový server. Pokud je cache správně nastavena, prohlížeč využívá zdroje primárně ze své cache.

Použití cache prohlížeče skrývá však jedno nebezpečí. Pokud si prohlížeč uloží zdroj do cache, jak jej později donutíme k aktualizaci cache? Nativním řešením je časová expirace. Zdroj je v cache uložen jen po nějakou dobu a pak si prohlížeč ověří, zda nebyl na serveru aktualizován. Pokud server vrátí prohlížeči HTTP status 304, je prohlížeči jasné, že zdroj nebyl modifikován a nadále využívá verzi ze své cache. Dotaz s výsledným statusem 304 sice trvá nějaký čas, ale pro velké zdroje je to stále znatelná úspora času – soubor nebyl přenášen po síti.

Pro většinu zdrojů svého webu můžete mít cache nastavenu s velmi dlouhou expirací, drtivá většina z nich se totiž i několik měsíců nemění. Pokud však změníte část svého CSS a provedete jeho minifikaci, chcete, aby si prohlížeč stáhl aktualizovaný soubor ihned. Můžete soubor přejmenovat, to však není příliš praktické pro častou aktualizaci a automatizaci této akce. Kouzelným tajemstvím je, že prohlížeč si do cache ukládá zdroje v kontextu celé jejich URL.

Náše CSS na URL:

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

tedy z pohledu prohlížeče není stejná jako CSS na URL:

<link href="/css/project.min.css?v=1" rel="stylesheet" type="text/css">

Přidáním prakticky bezvýznamného URL parametru „v=1″ jste si vynutili, že prohlížeč si CSS soubor stáhne jako zcela nový zdroj a opět uloží do cache po dobu expirace. Této technice se říká „cachebusting“.

Pojďme tedy donutit prohlížeče, aby využívali svou cache tak, jak chceme my. Příkazy pro řízení cache předává prohlížeči server. V drtivé většině případů je to dnes Apache server. Ten využívá konfigurační soubor .htaccess (bohužel některé hostingy jej nepovolují). Doplníme tedy svůj .htaccess o následující řádky, které zajistí normalizaci MIME typů a řízení cache mechanizmu:

# ---------------------------------------------------------
# Proper MIME type for all files
# CZ: Normalizace MIME typu pro vsechny soubory
# ------------------------------------------------------------

# JavaScript
#   Normalize to standard type (it's sniffed in IE anyways)
#   tools.ietf.org/html/rfc4329#section-7.2
AddType application/javascript         js

# Audio
AddType audio/ogg                      oga ogg
AddType audio/mp4                      m4a

# Video
AddType video/ogg                      ogv
AddType video/mp4                      mp4 m4v
AddType video/webm                     webm

# SVG
#   Required for svg webfonts on iPad
#   twitter.com/FontSquirrel/status/14855840545
AddType     image/svg+xml              svg svgz
AddEncoding gzip                       svgz

# Webfonts
AddType application/vnd.ms-fontobject  eot
AddType application/x-font-ttf         ttf ttc
AddType font/opentype                  otf
AddType application/x-font-woff        woff

# Assorted types
AddType image/x-icon                        ico
AddType image/webp                          webp
AddType text/cache-manifest                 appcache manifest
AddType text/x-component                    htc
AddType application/x-chrome-extension      crx
AddType application/x-opera-extension       oex
AddType application/x-xpinstall             xpi
AddType application/octet-stream            safariextz
AddType application/x-web-app-manifest+json webapp
AddType text/x-vcard                        vcf

# ----------------------------------------------------------------
# Expires headers (for better cache control)
# CZ: Hlavicky pro kontrolu nastaveni expirace
# ----------------------------------------------------------------

# These are pretty far-future expires headers.
# They assume you control versioning with cachebusting
# CZ: Toto jsou hlavicky s velmi dlouhou dobou expirace
# CZ: Predpokladaji ze vyuzivate cachebusting techniku

# Additionally, consider that outdated proxies may miscache
# CZ: Nektere zastarale proxy mohou cachovat chybne
#   www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/

<IfModule mod_expires.c>
  ExpiresActive on

# Perhaps better to whitelist expires rules? Perhaps.
  ExpiresDefault                          "access plus 1 month"

# cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5)
  ExpiresByType text/cache-manifest       "access plus 0 seconds"

# Your document html
  ExpiresByType text/html                 "access plus 0 seconds"

# Data
  ExpiresByType text/xml                  "access plus 0 seconds"
  ExpiresByType application/xml           "access plus 0 seconds"
  ExpiresByType application/json          "access plus 0 seconds"

# Feed
  ExpiresByType application/rss+xml       "access plus 1 hour"
  ExpiresByType application/atom+xml      "access plus 1 hour"

# Favicon (cannot be renamed)
  ExpiresByType image/x-icon              "access plus 1 week"

# Media: images, video, audio
  ExpiresByType image/gif                 "access plus 1 month"
  ExpiresByType image/png                 "access plus 1 month"
  ExpiresByType image/jpg                 "access plus 1 month"
  ExpiresByType image/jpeg                "access plus 1 month"
  ExpiresByType video/ogg                 "access plus 1 month"
  ExpiresByType audio/ogg                 "access plus 1 month"
  ExpiresByType video/mp4                 "access plus 1 month"
  ExpiresByType video/webm                "access plus 1 month"

# HTC files  (css3pie)
  ExpiresByType text/x-component          "access plus 1 month"

# Webfonts
  ExpiresByType application/x-font-ttf    "access plus 1 month"
  ExpiresByType font/opentype             "access plus 1 month"
  ExpiresByType application/x-font-woff   "access plus 1 month"
  ExpiresByType image/svg+xml             "access plus 1 month"
  ExpiresByType application/vnd.ms-fontobject "access plus 1 month"

# CSS and JavaScript
  ExpiresByType text/css                  "access plus 1 year"
  ExpiresByType application/javascript    "access plus 1 year"

</IfModule>

# ---------------------------------------------------------------
# ETag removal
# CZ: Odstraneni ETagu
# ---------------------------------------------------------------

# FileETag None is not enough for every server.
# FileETag None neni dostacujici pro vsechny servery.

<IfModule mod_headers.c>
  Header unset ETag
</IfModule>

# Since we're sending far-future expires, we don't need ETags for
# static content.
# CZ: Protoze nase expirace je daleko v budoucnu nepotrebujeme odesilat ETag pro staticky obsah.
# http://developer.yahoo.com/performance/rules.html#etags

FileETag None

Bonus – CDN a GZip

Pokud jste dávali pozor, možná už tušíte, proč mnoho webů využívá načítání jQuery z tzv. CDN (Content Delivery Network):

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
<script>window.jQuery || document.write('<script src="/js/jquery.min.js"><\/script>')</script>

Je-li knihovna jQuery linkována z CDN, je uložena v cache prohlížeče. Prohlížeč, který následně kdekoliv narazí na zdroj s touto URL, má proto jQuery ihned k dispozici. Všechny weby tak efektivně sdílejí tento statický zdroj v cache prohlížeče. CDN jsou navíc velmi rychlé a protože prohlížeče dnes implementují paralelní stahování zdrojů z různých adres, natažení jQuery z CDN je velmi dobrá cesta optimalizace pro rychlejší načítání webu. Protokol v URL směřující na CDN přitom není uveden záměrně.

I na CDN však může nastat výpadek. Proto je dobré doplnit i výše uvedený druhý řádek JavaScriptu, který testuje, zda je jQuery z CDN nataženo. Pokud není, vloží se do stránky <script> značka směřující na jQuery z lokálního serveru.

Velikost dat přenášených mezi serverem a prohlížečem by měla být co nejmenší. Moderní prohlížeče proto také implementují podporu pro příjem dat komprimovaných pomocí GZip komprese. Silnou stránkou GZipu je přitom komprese dat, ve kterých se často opakují stejné řetězce. Nejlépe se tedy uplatní v HTML, CSS, JavaScript, JSON, XML či HTC souborech.

Pokud váš web hoster podporuje Deflate modul serveru Apache prostřednictvím následujících řádků .htaccess konfigurace zapnete GZip kompresi přenášených dat:

# -------------------------------------------------------------
# Gzip compression
# -------------------------------------------------------------

  <IfModule mod_deflate.c>

  # Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
  <IfModule mod_setenvif.c>
    <IfModule mod_headers.c>
      SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
      RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
    </IfModule>
  </IfModule>

  # HTML, TXT, CSS, JavaScript, JSON, XML, HTC:
  <IfModule filter_module>
    FilterDeclare   COMPRESS
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/html
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/css
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/plain
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/x-component
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/javascript
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/json
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xhtml+xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/rss+xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/atom+xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/vnd.ms-fontobject
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $image/svg+xml
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $image/x-icon
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/x-font-ttf
    FilterProvider  COMPRESS  DEFLATE resp=Content-Type $font/opentype
    FilterChain     COMPRESS
    FilterProtocol  COMPRESS  DEFLATE change=yes;byteranges=no
  </IfModule>

  <IfModule !mod_filter.c>
    # Legacy versions of Apache
    AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
    AddOutputFilterByType DEFLATE application/javascript
    AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
    AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml
    AddOutputFilterByType DEFLATE image/x-icon image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype
  </IfModule>
</IfModule>

Tak a jsme v závěru. Gratuluji, naučili jste se 3 a 1 techniku, které vám pomohou dosáhnout rychlejší načítání webu. Mám za to, že uvedené postupy by měly být v repertoáru každého dobrého UI Developera. Je třeba zdůraznit, že optimalizace je často zbytečně nákladná. Mnohem efektivnější je začlenit kompresi a kompilaci zdrojů do standardního procesu vývoje UI. Téma automatizace vývojového workflow si však necháme na jindy.

Tomáš Kuba

Je webdesign generalista s oblibou v minimalismu, hrdý spoluautor Blábota a CTO ve společnosti Edgedesign.

Další články tohoto autora

Twitter

6 komentářů

  1. Twisťa
    Čec 30, 2012 @ 12:27:54

    Pěkné shrnutí

    Jen jednu technickou připomínku CDN je zkratkou Content Delivery Network :)

    Reply

    • Tomáš Kuba
      Čec 31, 2012 @ 18:54:18

      Děkuji – opraveno.

      Reply

  2. Daniel Krsiak
    Čec 30, 2012 @ 13:22:33

    dobry clanek pro novacky, kteri se zajimaji o technickou optimalizaci
    stara vec pro lidi, kteri v tom pracuji :)

    SASS
    co se slucovani souboru tyce, jde to delat i pres @import v .SCSS
    takze to vyplivne krasny 1 CSS soubor (bez importu), byt se sklada, treba z 10 pracovnic .SCSS

    Reply

    • Tomáš Kuba
      Čec 31, 2012 @ 19:02:37

      Díky za komentář. Jak je z článku patrné, interně používáme SASS. Cílem opravdu bylo poskytnout praktické rady, které lze uplatnit hned tam, kde jsou nejvíce potřeba. Profíci využívající SASS/LESS se z tohoto článku zřejmě moc nového nenaučí – souhlasím.

      Reply

  3. Carl114
    Srp 22, 2012 @ 20:50:52

    Témata kolem zrychlování mám rád a sám na stejné téma píšu, ale CDN nedoporučuji. Pokud nemáte nějaký opravdu špatný server tak zrychlení zas takové není a se sdíleným JS mám špatné zkušenosti. Když jsem si totiž dělal testy rychlosti tak došlo na to, že script u Googlu je pomalejší než ten u mě. Proto je dobré vždy testovat a opravdu se přesvědčit zda mi určitá technika opravdu pomohla.

    Je to asi půl roku zpět, co v cache řádku měli chybu a kvůli tomu se v některých prohlížečích třeba jquery kód ani nekešoval. Hned jak jsem si toho všiml tak jsem kód odstranil a začal znovu používat svůj. Takže za sebe můžu jen poradit – zkoušejte, ale nezapomínejte na testování zda a o kolik Vám určitá technika pomohla a jestli má smysl.

    Reply

    • Tomáš Kuba
      Zář 18, 2012 @ 11:52:20

      Rozhodně dobrá rada – testujte. Částečně tuto problematiku řeší trik automaticky vkládající link na lokální jQuery.

      Reply

Napsat komentář k Carl114 Zrušit