Vlastní JavaScriptový minifikátor

Minifikace se dělá, aby se zmenšil objem přenesených dat k uživatelům. Obvykle se minifikují JavaScript, CSS a občas i HTML. K tomu existují pro tuto akci určené nástroje – tzv. minifikátory. CSS a HTML se dají minifikovat víceméně pouze na úrovni odstranění bílých znaků, to v podstatě protože pokud byste chtěli zkracovat názvy, tak musíte refaktorovat strašně moc věcí, ať už stejné názvy v opačném formátu (např. ID definované v HTML se může vyskytovat i v CSS a běžně se tak i děje), tak i v mnohem horších (a hlavně nejednoznačných) textových konstantách v JavaScriptu. Proto je taková minifikace HTML a CSS prakticky nemožná. Minifikace na straně JavaScriptu je již lepší, tam si můžeme dovolit refaktoring (strojové přejmenování všech výskytů), tedy alespoň někde. Podívejme se na následující kód.

var promenna = 10;
function plus(hodnota) {
    var odmocnina = Math.sqrt(hodnota);
    promenna += odmocnina / hodnota;
}

Jak bychom ho mohli minifikovat? Když opomeneme odstranění konců řádků a dalších bílých znaků v podstatě můžeme zaměnit názvy parametrů a lokálních názvu za kratší. Např na:

var promenna = 10;
function plus(a) {
    var b = Math.sqrt(a);
    promenna += b / a;
}

Kód už je opticky kratší. Abychom si to upřesnili: přesně toto udělá běžný minifikátor, např. UglifyJS. Pokud bychom ještě odstranili konce řádků a bílé znaky (tak jako UglifyJS) a změřili úsporu, zjistíme, že oproti originálu jsme ušetřili 46,34% (zdroj: https://skalman.github.io/UglifyJS-online/) dat. Což je na tak banální akci poměrně dost. Já jsem si však řekl, že mi to nestačí a budu refaktorovat i na globalní úrovni. Toto si můžu dovolit pouze za splnění několika podmínek, které definují striktní psaní kódu a využívání (teda spíš nevyužívání) několika obvyklých postupů. V podstatě ten zásadní bod je že to v JavaScriptu ani nejde, protože JavaScript je jazyk kde krom toho že v proměnné nemusí být nic, popř. úplně vše se to ještě za běhu může měnit, což je dáno tím, že je to dynamicky typovaný jazyk. Takže je třeba to provést o úroven výš – třeba na TypeScriptu. Tam již refaktorovat lze (koneckonců je to implementovaná v kdejakém IDE) i na globální úrovni. Druhým bodem je, že se to bude jaksi blbě exportovat, když to bude mít minifikované názvy – takže použití je vesměs v opravdu globálních aplikací, můžete zapomenout na moduly. Nesmíte používat přímou stringovou reflexi – např.

var promenna = "hello world"
console.log(window["promenna"]) // vypíše hello world, po nejběžnějším refaktoringu vypíše error

nelze refaktorovat ze stejného důvodu jako ani HTML a CSS nejde. A mnoho dalších důvodů, které můj minifikátor rozbijou (resp. výstup bude nefunkční). Když to vše zkousnete, tak pak můžete zminifikovat úplně vše (krom externích názvů). Včetně názvu promenna, takže kód by vypadal nějak následovně.

var c = 10;
function d(a) {
    var b = Math.sqrt(a);
    c += b / a;
}

Po odstranění bílých znaků dosáhneme výsledku minifikace o 59.016%. Tak nízké skóre je dáno především tím, že se jedná o malý soubor s málo závislostmi. Při pohledu na výstup, např. na tento úryvek z výstupu kdy minifikátor minifikoval sám svůj zdrojový kód:

Lze docela dobře vidět, že zbyly buď běžně používané výrazy jako String, toString(), Property a hlavně také klíčová slovy, tím že vyskytovaná slova jsou víceméně pořád dokola ty stejné, což naznačuje že zde bude velmi výhodná komprimace GZIP při přenosu. Když se to sečte (globální refaktoring + UglifyJS pro doladění zbytku + GZIP) skript jasně překonává všechny ostatní, zatímco u UglivyJS se udává minifikace v rozmezí 70-90%, můj skript tyto hodnoty hravě překonává. Sám sebe sice zminifikoval o „pouhých“ 81%, ale jednu moji aplikaci (více dokumentovanou než tento minifikátor, který má 2 řádky komentáře) se podařilo zminifikovat o 90.190%. V následujícím blogovém zápisku najdete informace, jak interně funguje, co používá, proč to používá, atd…