Efektivní minifikace Javascriptu

Posted 30. 11. 2014 / By Petr Soukup / Vývoj

yui-compressor Minifikujete javascript před publikováním na web? Ano? Výborně! A víte, že můžete ušetřit ještě mnohem více?

Minifikátory

Minifikátorů dnes existuje spousta - UglifyJS, Google Closure Compiler, MinifyJs, ... Nejvíce se mi osvědčil YUI compiler, takže ho budu používat pro všechny příklady. Můžete si ho vyzkoušet online zde. Pro lepší čitelnost budu u minikovaných verzí vracet formátování (nové řádky, odsazení).

Do minifikátoru pošlete javascript a on z něj odstraní zbytečné středníky, bílé znaky, komentáře a všechno ostatní, co není nutně potřeba. Tím hravě srazí velikost souboru třeba až o polovinu.

Před minifikací

Pro test zkusím tento kód, který bude 50x za sebou (ať jsou trochu reálnější čísla):

if(document.getElementById('someId1')){
     document.getElementById('someId1').setAttribute('href','https://google.com');
     document.getElementById('someId2').setAttribute('href','https://translator.google.com');
     if(document.getElementById('someId3')){
          document.getElementById('someId3').setAttribute('href','https://google.com');
          document.getElementById('someId4').setAttribute('href','https://translator.google.com');
     }
}
// 50x to samé znovu

Po minifikaci

if(document.getElementById("someId1")){document.getElementById("someId1").setAttribute("href","https://google.com");document.getElementById("someId2").setAttribute("href","https://translator.google.com");if(document.getElementById("someId3")){document.getElementById("someId3").setAttribute("href","https://google.com");document.getElementById("someId4").setAttribute("href","https://translator.google.com")}};

Velikost před minifikací: 22 900 B Velikost po minifikaci: 20 451 B

Ušetřili jsme tak úžasných 2 449 bajtů. Paráda! Ale nešlo by ušetřit více?

Zapouzdření proměnných

Minifikátoru výrazně pomůžeme, když celý náš kód zabalíme do anonymní funkce. Díky tomu náš kód nemůže vytvářet globální proměnné. Tato technika je proto použitá třeba v jQuery nebo v jakékoliv slušně napsané knihovně. Aniž bychom muselo cokoliv měnit v kódu, hned nám umožní výrazně ho zkrátit.

Před minifikací

(function(document){
   if(document.getElementById('someId1')){
        document.getElementById('someId1').setAttribute('href','https://google.com');
        document.getElementById('someId2').setAttribute('href','https://translator.google.com');
        if(document.getElementById('someId3')){
             document.getElementById('someId3').setAttribute('href','https://google.com');
             document.getElementById('someId4').setAttribute('href','https://translator.google.com');
        }
   }
   // 50x to samé znovu
})(document)

Po minifikaci

(function(a){
   if(a.getElementById('someId1')){
        a.getElementById('someId1').setAttribute('href','https://google.com');
        a.getElementById('someId2').setAttribute('href','https://translator.google.com');
        if(a.getElementById('someId3')){
             a.getElementById('someId3').setAttribute('href','https://google.com');
             a.getElementById('someId4').setAttribute('href','https://translator.google.com');
        }
   }
   // 50x to samé znovu
})(document)

Velikost před minifikací: 24 783 B Velikost po minifikaci: 18 376 B

Nyní jsme ušetřili 4 524 bajtů (oproti první verzi), přestože je původní kód delší. Trik je v tom, že místo globální proměnné "document" jsem z ní vyrobil lokální. Díky tomu ji minifikátor může bezpečně přejmenovat na něco kratšího.

Zapouzdření funkcí

Pořád je ale vidět, že spoustu místa zabírá funkce "getElementById", kterou minifikátor nemůže bezpečně přejmenovat. Tak mu pomůžeme!

Před minifikací

(function(document){
   function documentGetElementById(id){
       return document.getElementById(id);
   }
   if(documentGetElementById('someId1')){
        documentGetElementById('someId1').setAttribute('href','https://google.com');
        documentGetElementById('someId2').setAttribute('href','https://translator.google.com');
        if(documentGetElementById('someId3')){
             documentGetElementById('someId3').setAttribute('href','https://google.com');
             documentGetElementById('someId4').setAttribute('href','https://translator.google.com');
        }
   }
   // 50x to samé znovu
})(document)

Po minifikaci

// přidávám nové řádky a odsazení pro lepší čitelnost
(function(a){
   function b(c){return a.getElementById(c)}
   if(b('someId1')){
        b('someId1').setAttribute('href','https://google.com');
        b('someId2').setAttribute('href','https://translator.google.com');
        if(b('someId3')){
             b('someId3').setAttribute('href','https://google.com');
             b('someId4').setAttribute('href','https://translator.google.com');
        }
   }
   // 50x to samé znovu
})(document)

Velikost před minifikací: 24 983 B Velikost po minifikaci: 13 917 B

Nyní jsme ušetřili 8 893 bajtů (44 %) (oproti první verzi). Samozřejmě by šlo ušetřit i více, ale to už budeme narážet na to, že je to jen umělý příklad.

GZIP

Ušetřili jsme sice 44%, ale javascript bude v reálném nasazení servírovaný s GZIP kompresí. Bude to mít na výsledek vliv?

Celkem bajtů Úspora bajtů Úspora procent
Bez minifikace 22 600 B --- ---
Minifikace 13 917 B 8 893 B 44%
Bez minifikace (GZIP) 271 B --- ---
Minifikace (GZIP) 254 B 16 B 6%
GZIP vs no GZIP

Při použití komprese je naše úspora najednou jen 6%. Jak je to možné? Komprese hledá například opakující se řetězce a nahrazuje je. Dělá tak vlastně to samé, co pracně nutíme dělat minifikátor. Tady jsme navíc kompresi hodně pomohli tím, že se opakuje 50x stejný blok textu, takže v reálném nasazení se to bude chovat jinak.

Vyplatí se to?

Na příkladu je vidět, že bez větší námahy lze ušetřit hodně dat. Problém je, že je nutné na to neustále myslet a používat různé obezličky jako například absurdní funkci documentGetElementById. Při použití GZIP je navíc vidět, že to může být zbytečná práce. Má tedy smysl se tím zabývat?

Neřekl bych, že se vyplatí ohýbat celý proces vývoje kvůli ušetřeným pár bajtům. Je ale určitě dobré vědět, jakým způsobem minifikátor pracuje. Například trik s převodem "document" na lokální proměnnou nevyžaduje žádný zásah do kódu a přesto může přinést výraznou úsporu dat.

Trochu jiná situace bude třeba u měřících kódů - ty jsou nasazovány na spousty webů, takže ušetřený bajt může znamenat gigabajt denního trafficu.

 

Další díly:

  1. Jak se loví milisekundy (nejen v #nettefw)
  2. Optimalizujeme pro rychlost: Obrázky
  3. Optimalizujeme pro rychlost: HTTPS
  4. Efektivní minifikace Javascriptu
  5. Optimalizujeme: critical+asynchronní CSS


O blogu
Blog o provozování eshopů a technologickém zázemí.
Aktuálně řeším hlavně cloud, bezpečnost a optimalizaci rychlosti.

Rozjíždím službu pro propojení eshopů s dodavateli.