Geek #17

A co kdybychom dali dohromady partu expertů, kteří se podílí na tvorbě velkých webových stránek, abychom vytvořili definitivní příručku pro výkon front-endu?

A ne jen tak nějakou nudnou příručku vytvořenou pro roboty, co kdybychom vytvořili něco zábavného? Co třeba dát dohromady lidi jako Briza Bueno (Americanas.com), Davidson Fellipe (Globo.com), Giovanni Keppelen (ex-Peixe Urbano), Jaydson Gomes (Terra), Marcel Duran (Twitter), Mike Taylor (Opera), Renato Mangini (Google), a Sérgio Lopes (Caelum), aby měla naše příručka pořádné reference?

To je přesně věc, kterou jsme udělali! A povedeme vás tímto bojem za vytvoření ještě rychlejších stránek.

Zeno Rocha, vedoucí projektu.

Opravdu na výkonu záleží?

??

Jasně, že záleží a vy to víte. Tak proč vytvářet pomalé stránky, které vedou ke špatnému uživatelskému prožitku? Toto je komunitní příručka praktických tipů, která vám ukáže, jak dělat webové stránky rychlejší. Neplýtvejme čas ukazováním případových studií a pojďme rovnou na věc!

HTML

Vyhněte se inline/vloženému kódu

Existují tři základní cesty, jak vložit CSS nebo JavaScript do stránky:

1) Inline: CSS je definováno uvnitř style atributu a JavaScript například uvnitř onclick atributu u libovolné HTML značky.

2) Vložené: CSS je definováno uvnitř <style> tagu a JavaScript uvnitř <script> tagu.

3) Externí: CSS je nahráno z <link> a JavaScript z src atributu tagu <script>.

První dvě varianty, nehledě na to, že snižují počet HTTP požadavků, ve skutečnosti zvětšují velikost HTML dokumentu. Přesto to může být výhodné, pokud máte malé soubory a náklady na request jsou větší problém. V takovém případě spusťe testy a vyhodnoťte, zda nastane zrychlení. Také zvažte smysl vaší stránky a její návštěvnost: pokud očekáváte, že uživatel navštíví stránku jen jednou, například při nějaké dočasné kampani, kde nečekáte, že se uživatel bude vracet, inlinový/vložený kód pomůže snížit počet HTTP požadavků.

> Vyhněte se psaní CSS/JS uvnitř vašeho HTML (raději jej automatizujte pomocí nástrojů).

Třetí varianta nejen zlepšuje organizaci vašeho kódu, ale také umožňuje prohlížečům aktivovat kešování. Tato varianta by měla být preferována pro naprostou většinu případů, zvláště pokud pracujete s velkými soubory a mnoha stránkami.

> Užitečné nástroje / Reference

Styly nahoru/scripty dolů

Když vložíme styly do <head>, umožníme tak progresivní vykreslení stránky. Uživateli se tak zdá, že se stránka načítá rychleji.

<head>
  <meta charset="UTF-8">
  <title>Browser Diet</title>

  <!-- CSS -->
  <link rel="stylesheet" href="style.css" media="all">
</head>

Pokud ale vložíme styly na konec stránky, nejprve dojde k vykreslení beze stylů, až poté jsou styly staženy a aplikovány.

Geek #32

Na druhou stranu když jde o JavaScript, je důležité umístit skripty na spodek stránky, protože ty blokují vykreslování stránky, zatímco jsou načítány a spouštěny.

<body>
  <p>Lorem ipsum dolor sit amet.</p>

  <!-- JS -->
  <script src="script.js"></script>
</body>

> Reference

Zkuste async

Abychom vysvětlili, jak je tento atribut užitečný pro lepší výkon, je dobré pochopit, co se děje, když jej nepoužijeme.

Geek #20
<script src="example.js"></script>

V tomto případě musí stránka čekat, dokud není skript celý stažený, naparsovaný a spuštěný, před tím, než může být zpracováno a vykresleno další HTML. To může značně prodloužit čas načítání vaší stránky. Občas je takové chování požadováno, obecně však ne.

<script async src="example.js"></script>

Skript je stažen asynchronně, zatímco pokračuje parsování zbytku stránky. Je garantováno, že se skript spustí hned, jak bude stažen. Mějte na mysli, že v případě více asynchronních skriptů není nijak zaručeno, v jakém pořadí budou spuštěny.

> Reference

CSS

Minimalizujte vaše kaskádové styly

Pro udržení čitelného kódu je rozumné psát komentáře a používat odsazení:

.center {
  width: 960px;
  margin: 0 auto;
}

/* --- Structure --- */

.intro {
  margin: 100px;
  position: relative;
}

Ale prohlížeči na ničem z toho nezáleží. Proto vždy pamatujte na minimalizaci vašich CSS pomocí automatizovaných nástrojů.

.center{width:960px;margin:0 auto}.intro{margin:100px;position:relative}

Minimalizace ořeže přebytečné bajty ze souboru, což vede k rychlejšímu stažení, parsování a spuštění.

Pro ty, kteří používají pre-procesory jako Sass, Less, a Stylus, je možné nakonfigurovat, aby byl kompilovaný CSS výstup minimalizován.

> Užitečné nástroje / Reference

Sloučení více CSS souborů

Další doporučovaná technika pro organizaci a správu stylů je rozdělení na modulární komponenty.

Geek #9
<link rel="stylesheet" href="structure.css" media="all">
<link rel="stylesheet" href="banner.css" media="all">
<link rel="stylesheet" href="layout.css" media="all">
<link rel="stylesheet" href="component.css" media="all">
<link rel="stylesheet" href="plugin.css" media="all">

Pak je ale třeba pro každý takový soubor jeden HTTP request (a je známo, že prohlížeče mohou stahovat jen omezený počet zdrojů souběžně).

<link rel="stylesheet" href="main.css" media="all">

Tak tedy kombinujte svá CSS. Menší počet souborů vede k menšímu počtu requestů a rychlejšímu načtení stránky.

Chcete využít to nejlepší z obou světů? Automatizujte tento proces pomocí buidlovacích nástrojů.

> Užitečné nástroje / Reference

Preferujte <link> před @import

Existují dvě varianty, jak vložit externí styl do stránky: buď pomocí značky <link>:

<link rel="stylesheet" href="style.css">

nebo pomocí direktivy @import (uvnitř externího stylu nebo ve značce <style>):

@import url('style.css');

Pokud použijete druhou variantu v externím stylu, prohlížeč není schopen stahovat soubor paralelně, což může způsobit zdržení při stahování dalších souborů.

> Reference

JavaScript

Nahrávejte obsah třetích stran asynchronně

Kdo nikdy nenahrával obsah třetích stran k vložení Youtube videa nebo like/tweet tlačítka?

Geek #46

Velký problém je, že tyto kódy nejsou vždy nahrávány efektivně. Ať už z důvodu špatného připojení uživatele nebo serveru, kde jsou hostovány. Nebo tato služba může být dočasně mimo provoz, blokována uživatelem nebo firemním firewallem.

V případě takových problémů může dojít k výrazným komplikacím při nahrávání stránky, případně úplnému zablokování načítání. Tyto skripty by proto měly vždy být načítány asynchronně (nebo používejte Friendly iFrames).

var script = document.createElement('script'),
    scripts = document.getElementsByTagName('script')[0];
script.async = true;
script.src = url;
scripts.parentNode.insertBefore(script, scripts);

Alternativně, pokud chcete nahrávat více widgetů třetích stran, můžete je nahrát asynchronně pomocí tohoto skriptu.

> Video / Reference

Kešujte délku pole

Smyčka je nepochybně jedna z nejpodstatnějších částí ovlivňujících výkon JavaScriptu. Zkuste optimalizovat logiku uvnitř smyčky tak, aby každá iterace byla vykonána užitečně.

Jedna z cest, jak toho dosáhnout, je uložit si délku pole, se kterým se pracuje, aby nemusela být opakovaně zjišťována v každé iteraci.

var arr = new Array(1000),
    len, i;

for (i = 0; i < arr.length; i++) {
  // Spatne - velikost musi byt prepocitana 1000x
}

for (i = 0, len = arr.length; i < len; i++) {
  // Dobre - velikost je zjistena jen jednou a ihned ulozena
}

> Výsledky na JSPerf

> Poznámka: Přestože moderní prohlížeče automaticky optimalizují tento proces, stále je tato doporučovaná technika užitečná pro starší prohlížeče.

Při iterování přes kolekce v HTML, jako jsou seznamy Nodů (NodeList) vytvořené například pomocí volání document.getElementsByTagName('a') je celá věc ještě více kritická. Tyto kolekce jsou považovány za "živé" , protože se automaticky aktualizují, když nastane změna v elementu, ke kterému patří.

var links = document.getElementsByTagName('a'),
    len, i;

for (i = 0; i < links.length; i++) {
  // Spatne - pri kazde iteraci dojde k opetovnemu vypoctu delky pole, kdyby se nahodou zmenila
}

for (i = 0, len = links.length; i < len; i++) {
  // Dobre - delka pole je jednou ziskana a ulozena, pote se v iteraci pouziva jen ta predpoctena
}

// Desive: ukazka nekonecne smycky
for (i = 0; i < links.length; i++) {
  document.body.appendChild(document.createElement('a'));
  // Pri kazde iteraci naroste seznam linku, nikdy tak nedojde k naplneni podminky pro ukonceni cyklu
  // to by se nestalo, kdyby se velikost pole predem ulozila do promenne a pak pouzila ve smycce
}

> Reference

Vyhněte se document.write

Použití document.write vytváří závislost na kompletním načtení stránky.

Tato (špatná) praktika je developery už roky zavrhována, přesto ale existují případy, kdy je potřebná. Třeba jako synchronní fallback pro některé JavaScriptové soubory.

HTML5 Boilerplate, například používá tuto techniku pro načtení jQuery lokálně, pokud CDN od Google není dostupná.

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

> Pozor: document.write vykonaný během nebo po window.onload události nahradí kompletní obsah současné stránky.

<span>foo</span>
<script>
  window.onload = function() {
    document.write('<span>bar</span>');
  };
</script>

Výsledek finální stránky bude pouze bar a nikoliv foobar, jak by se dalo čekat. To samé nastane, pokud se spustí po události window.onload.

<span>foo</span>
<script>
  setTimeout(function() {
    document.write('<span>bar</span>');
  }, 1000);
  window.onload = function() {
    // ...
  };
</script>

Výsledek bude stejný jako předcházející příklad, pokud se funkce naplánovaná pomocí setTimeout vykoná po události window.onload.

> Reference

Minimalizujte Repaints a Reflows

Repaints a reflows se vyskytují v případě, že existuje proces překreslující DOM, když se změní nějaká vlastnost elementu.

Repaint je spuštěn v případě, že se změní vzhled elementu bez toho, aby se změnil jeho layout. Nicole Sullivan jako příklad uvádí změnu stylů, třeba background-color.

Reflow je mnohem nákladnější, protože je způsoben změnou layoutu stránky, jako je například změna šířky elementu.

Není pochyb, že je dobré se těmto náročným repaints a reflows vyhnout, takže například místo tohoto:

Geek #55
var div = document.getElementById("to-measure"),
    lis = document.getElementsByTagName('li'),
    i, len;

for (i = 0, len = lis.length; i < len; i++) {
  lis[i].style.width = div.offsetWidth + 'px';
}

zkuste toto:

var div = document.getElementById("to-measure"),
    lis = document.getElementsByTagName('li'),
    widthToSet = div.offsetWidth,
    i, len;

for (i = 0, len = lis.length; i < len; i++) {
  lis[i].style.width = widthToSet + 'px';
}

Když nastavíte style.width, prohlížeč musí přepočítat layout. Obvykle je několik změn stylů elementů spojeno do jednoho reflow, protože to prohlížeč nemusí řešit, dokud nedojde k aktualizaci obrazovky. Každopádně v prvním příkladě jsme používali offsetWidth, což je šířka layoutu elementu, prohlížeč tedy musí přepočítat layout.

Pokud potřebujete číst data layoutu ze stránky, udělejte tak naráz - předtím, než začnete nastavovat cokoliv, co mění layout, jak je ukázáno v druhém příkladě.

> Demo / Reference

Vyhněte se zbytečným manipulacím v DOM

Pokaždé, když sáhnete na DOM zbytečně, jedno koťátko zemře.

Vážně, průchod DOMem je nákladný. I přes to, že jsou dnes JavaScriptové enginy výrazně rychlejší a vykonnější, vždy se snažte optimalizovat dotazy nad DOM.

Jedna z nejužitečnějších rad zní: když přistupujete k elementu více než jedenkrát, uložte si jej do proměnné, ať se nemusíte dotazovat DOMu vícekrát.

// opravdu spatne!
for (var i = 0; i < 100; i++) {
  document.getElementById("myList").innerHTML += "<span>" + i + "</span>";
}
// mnohem lepsi :)
var myList = "";

for (var i = 0; i < 100; i++) {
  myList += "<span>" + i + "</span>";
}

document.getElementById("myList").innerHTML = myList;
// mnohem mnohem lepsi :)
var myListHTML = document.getElementById("myList").innerHTML;

for (var i = 0; i < 100; i++) {
  myListHTML += "<span>" + i + "</span>";
}

> Výsledky na JSPerf

Minimalizujte vaše scripty

Stejně jako u CSS, k udržení čitelného kódu je dobré psát komentáře a odsazovat:

BrowserDiet.app = function() {

  var foo = true;

  return {
    bar: function() {
      // do something
    }
  };

};

Ale prohlížeči na ničem z toho nezáleží. Proto vždy pamatujte na minimalizaci vašich JavaScriptů pomocí automatizovaných nástrojů.

BrowserDiet.app=function(){var a=!0;return{bar:function(){}}}

Minimalizace ořeže přebytečné byty ze souboru, což vede k rychlejšímu stažení, parsování a spuštění.

> Užitečné nástroje / Reference

Slučujte více JS souborů do jediného

Další doporučovanou technikou pro organizaci a správu skriptů je rozdělení na modulární komponenty.

Geek #47
<script src="navbar.js"></script>
<script src="component.js"></script>
<script src="page.js"></script>
<script src="framework.js"></script>
<script src="plugin.js"></script>

Pak je ale třeba pro každý takový soubor jeden HTTP request (a je známo, že prohlížeče mohou stahovat jen omezený počet zdrojů souběžně).

<script src="main.js"></script>

Tak tedy kombinujte své JS. Menší počet souborů vede k menšímu počtu requestů a rychlejšímu načtení stránky.

Chcete využít to nejlepší z obou světů? Automatizujte tento proces pomocí buildovacích nástrojů.

> Užitečné nástroje / Reference

jQuery

Vždy používejte nejnovější verzi jQuery

Vývojáři jádra JQuery se stále snaží vylepšovat knihovnu, ať už jde o lepší čitelnost kódu, nové funkce nebo optimalizace stávajících algoritmů.

Geek #36

Proto vždy používejte nejnovější verzi JQuery. Dostupná je zde, pokud si ji chcete uložit k sobě do souboru:

http://code.jquery.com/jquery-latest.js

Nikdy ji ale z této URL neodkazujte v tagu <script>, můžete si tak zadělat na problémy, protože je vždy poskytována nejnovější verze dříve, než máte šanci ji otestovat. Místo toho raději linkujte poslední verzi, kterou máte odzkoušenou.

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>

Tak, jak říká moudrý Barney Stinson, "Nové je vždy lepší" :P

> Reference

Selektory

Selektory jsou jednou z nejdůležitějších věcí při použití JQuery. Existuje mnoho různých cest, jak vybrat element v DOMu. To ale neznamená, že jsou všechny stejně výkonné. Můžete vybrat element pomocí třídy, ID, metod jako find(), children().

Mezi všemi, výběr podle ID je nejrychlejší, protože je založen na nativní operaci nad DOM:

$("#foo");

> Výsledky na JSPerf

Používejte for namísto each

Využití nativních funkcí JavaScriptu téměř vždy vede k rychlejšímu provedení než obdoby v JQuery. Z toho důvodu raději než metodu jQuery.each používejte nativní for smyčku.

Ale pozor na to, že i když je for in nativní, v mnoha případech je výkon horší, než jQuery.each.

Těžce zkoušená smyčka for nám dává další příležitost k urychlení věcí pomocí kešování délky kolekce, přes kterou iterujeme.

for ( var i = 0, len = a.length; i < len; i++ ) {
    e = a[i];
}

Použití obráceného while a obrácené for smyčky je žhavé téma v komunitě a je často citováno jako nejrychlejší forma iterace. Kvůli špatné čitelnosti se ale používá jen výjimečně.

> Výsledky na JSPerf / Reference

Nepoužívejte JQuery za každou cenu...

Občas může být čistý JavaScript jednodušší a výkonější než jQuery.

Geek #6

Zvažte následující HTML:

<div id="text">Let's change this content</div>

Místo, abyste napsali:

$('#text').html('The content is changed').css({
  backgroundColor: 'red',
  color: 'yellow'
});

použijte prostý JavaScript:

var text = document.getElementById('text');
text.innerHTML = 'The text is changed';
text.style.backgroundColor = 'red';
text.style.color = 'yellow';

Je to jednoduché a mnohem rychlejší.

> Výsledky na JSPerf / Reference

Obrázky

Použijte CSS Sprites

Tato technika se zabývá skládáním různých obrázků do jediného souboru.

CSS Sprite Example

A jejich následném pozicování pomocí CSS.

.icon-foo {
  background-image: url('mySprite.png');
  background-position: -10px -10px;
}

.icon-bar {
  background-image: url('mySprite.png');
  background-position: -5px -5px;
}

Výsledkem je zásadní snížení počtu HTTP požadavků a nehrozí tak zdržování načítání dalších zdrojů vašich stránek.

Když používáte sprite, vyhněte se příliš velkým bílým místům mezi obrázky. Sice to neovlivňuje velikost souboru, má to ale vliv na spotřebu paměti.

I přes to, že skoro každý techniku spritů zná, není její použití široce rozšířeno—pravděpodobně proto, že vývojáři nepoužívají automatizační nástroje pro generování spritů. Vypíchli jsme několik takových, které vám s tím mohou pomoci.

> Užitečné nástroje / Reference

Data URI

Tato technika je alternativou k používání CSS spritů.

Data-URI je způsob, jak inlinově vložit obsah namísto jeho odkazování pomocí URI, jak je to běžné. V tomto případě to použijeme k inlinovému vložení obrázku ve vlastnosti CSS background. Snížíme tak počet HTTP požadavků, které stránka potřebuje.

Předtím:

.icon-foo {
  background-image: url('foo.png');
}

Potom:

.icon-foo {
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII%3D');
}

Všechny prohlížeče od IE8 a vyšší podporují techniku kódování pomocí base64.

Stejně tak jako CSS sprity, i tato metoda potřebuje nástroje, které činnost zautomatizují v okamžiku buildu. Tato metoda je výhodná v tom, že obrázky mohou být každý ve svém souboru a při vývoji tak nekomplikuje práci.

Naopak nevýhoda je ve značně narůstající velikosti HTML/CSS, jestliže máte velké obrázky. Využití metody se nedoporučuje, pokud nepoužíváte gzipování vašich HTML/CSS. Režie pro zakódování a vložení do stránky může negativně převážit zrychlení získané nižším počtem HTTP požadavků.

> Užitečné nástroje / Reference

Neměňte velikost obrázků ve stránce

Vždy definujte atributy width a height obrázku. To zabrání zbytečnému překreslování stránky během renderování.

<img width="100" height="100" src="logo.jpg" alt="Logo">

John Q. Developer to ví, a má obrázek o rozměrech 700x700px. Rozhodne se proto tuto techniku použít pro zobrazení obrázku jako 50x50px.

Mr. Developer si ale neuvědomuje, že jsou zcela zbytečně přenášeny desítky kilobytů navíc.

Vždy mějte na mysli: jen proto, že je možné definovat výšku a šířku obrázku, neznamená to, že byste tuto věc měli použít pro změnšení obrázků.

> Reference

Optimalizujte obrázky

Soubory obrázků obsahují spoustu informací, které jsou zbytečné na webu. Například JPEG fotka může obsahovat Exif metadata z foťáku (datum, typ foťáku, geolokace a pod.). PNG obsahuje informace o barvách, metadata a občas i vložený náhled obrázku. Nic z toho není používáno prohlížečem a zbytečně to nafukuje velikost souboru.

Existují nástroje pro optimalizaci obrázku, které odstraní nepotřebná data a výsledkem je menší soubor i bez ztráty kvality. Říkáme tomu bezztrátová komprese.

Další možnost, jak optimalizovat obrázky, je komprese na vrub vizuální kvality. Tomu říkáme ztrátová optimalizace. Například, když exportujete JPEG, můžete si zvolit úroveň kvality (číslo mezi 0 a 100). Když jde o výkon, vždy volte nejnižší možné číslo, které stále ještě zachová přijatelnou vizuální podobu. Další běžná ztrátová technika je zredukování barevné palety v PNG nebo konverze PNG-24 na PNG-8.

K vylepšení vjemového výkonu je vhodné převést všechny JPEG soubory na jejich progresivní varianty. Progresivní JPEGy mají skvělou podporu v prohlížečích, je snadné vytvořit je a nemají žádné výkonové nevýhody. Výhodou je, že se obrázek načte na stránce dříve (viz demo).

> Užitečné nástroje / Reference

Bonus

Diagnostické nástroje: váš nejlepší přítel

Geek #43

Pokud chcete proniknout do světa optimalizací výkonu webu, je zásadní nainstalovat si YSlow rozšíření do prohlížeče—to budou od teď vaši nejlepší přátelé.

Pokud preferujete online nástroje, navštivte WebPageTest, HTTP Archive, nebo PageSpeed.

V zásadě každý nástroj analyzuje výkon stránky a vytvoří report, který stránku ohodnotí a přidá neocenitelné rady, které pomohou vyřešit případné problémy.

A to je pro dnešek vše!

Geek #31

Doufáme, že po přečtení této příručky dovedete dát vaše stránky do formy. :-)

A nezapomeňte, jak už to v životě chodí, neexistuje nic takového, jako stříbrná kulka. Ladění výkonu vaší aplikace je chvályhodný počin, neměl by být ale tím jediným, co při vývoji řešíte—celou dobu musíte zvažovat náklady a přidanou hodnotu.

Chcete se dozvědět více? Mrkněte na zdroje, které jsme použili k napsání této příručky.

Máte tipy? Tweetněte je na @BrowserDiet nebo udělejte pull request na Githubu.

Nezapomeňte se podělit se svými přáteli, pojďme vytvořit rychlejší weby pro všechny.