Přejít k navigační liště

Zdroják » JavaScript » Minifikace JavaScriptu

Minifikace JavaScriptu

Články JavaScript, Různé

Hledáte vhodný zmenšovač JS kódu a napadá vás, že by možná nejjednodušší bylo napsat si něco vlastního, než přizpůsobovat svůj toolchain některému z existujících zmenšovačů? Nebo že se vám nelíbí licence, pod kterou je zmenšovač šířen? Možná vám následující článek ušetří mnoho hodin vlastní práce!

Přiznám se, že cizí knihovny nepoužívám moc ochotně. Nejdřív strávím spoustu času hledáním té, která by mi vyhovovala. Pak často zjistím, že mi stejně něčím nevyhovuje. Když to opravím, pošlu změnu autorovi a on ji přijme, tak čekám na vydání nové verze. Když vyjde, tak musím otestovat, jestli se neporouchalo něco jiného. Cizí knihovny taky často mají závislosti (např. na Javě), které já vyžadovat nechci.

Často si proto radši něco spíchnu sám, protože s tím mám nakonec míň starostí, strávím tím míň času a dělám to, co mě baví mnohem víc, než hledat, zkoušet a domlouvat.

Když jsem hledal minifikátor JavaScriptu pro použití v Admineru, tak jsem se podíval na stávající řešení a vybral JSMin pro PHP. Ten mi v zásadě vyhovoval: neměl žádné závislosti, dělal skoro přesně to, co jsem chtěl, aktualizace probíhaly hladce. Navíc v podobný čas jako Adminer přešel ze SVN na Git, takže i provázání repozitářů bylo bez problémů.

Než…

Než se dobrá duše rozhodla Adminer zpřístupnit jako balík Debianu. JSMin má totiž celkem běžnou licenci, ale s nezvyklým dovětkem: “The Software shall be used for Good, not Evil.” A podle dogmatiků to není dost svobodné, protože chudáci zlořádi teď nemůžou využívat JSMin pro páchání zla. Dokonce by se projekty využívající JSMin neměly hostovat na Google Code.

Když by autor změnil slovo shall na should, tak by se všechno vyžehlilo, ten o tom ale nechce ani slyšet (a já se mu nedivím).

Takže jsem byl slušně požádán, jestli bych nemohl JSMin z Admineru vyhodit. A i když existují jiné kvalitní nástroje, především Google Closure Compiler, tak než to zase řešit, rozhodl jsem se jako obvykle, že si něco spíchnu sám.

JsShrink

Úlohu jsem vyřešil pomocí regulárního výrazu. Ten přeskakuje všechno, co má v JavaScriptu zvláštní význam (řetězce uzavřené do uvozovek nebo do apostrofů a regulární výrazy uzavřené do lomítek). Ze zbytku odstraňuje mezery a komentáře, případně je nahradí novým řádkem, pokud by došlo k nežádoucímu slepení, např. v kódu var a. Mimochodem nový řádek je mnohem důmyslnější než mezera, která se používá obvykle – zabírá shodně jeden bajt a nevytváří kilometr dlouhé řádky, takže se dá kód v nouzi i ladit a nepřekáží při prohledávání.

Nejsložitější jsou JavaScriptové regulární výrazy. Jejich oddělovač koliduje s operátorem dělení a se začátkem komentářů, takže je potřeba sledovat kontext, ve kterém se lomítko vyskytuje. Navíc ani neošetřené lomítko ještě regulární výraz nutně neukončuje: /[/]/ je platný regulární výraz.

Funkce vyžaduje příkazy ukončené středníkem, nespokojí se s jejich ukončením novým řádkem (to mimochodem nesplňují frameworky Dojo a Ext JS). Jinak by se měla vypořádat i s těmi největšími špeky. Např. JSMin nepodporuje platný výraz a++ + +2, zmiňuje to dokonce i v dokumentaci. Dojo ShrinkSafe si zase vyláme zuby na výrazu 8 / /.*/ / 2. Přijde vám výraz nesmyslný? Jistě, ale validní JavaScript to je. Navíc může vracet platný výsledek po následující definici:

RegExp.prototype.toString = function () {
    return 2;
};

ShrinkSafe si ostatně vyláme zuby i na zmíněném /[/]/ a a++ + +2 a chvalozpěv na domácí stránce o tom, jak je tento nástroj bezpečný, protože nepoužívá křehké regulární výrazy, mi přijde poněkud nemístný.

Implementace v JavaScriptu

Regulární výraz je tak jednoduchý (např. nepoužívá lookbehind aserce), že se dá převést i do JavaScriptu, takže si ho můžete vyzkoušet přímo ve svém prohlížeči.

Srovnání

Popis dostupných nástrojů a popis, proč JavaScript zkracovat, vyšel článek na Zdrojáku.

Kód Original JsShrink JSMin ShrinkSafe GCC WS GCC Adv YUI
var a = 1; 10 8 9 10 8 0 8
a++ + +2; 9 9 chyba chyba 9 7 chyba
8 / / .* / / 2; 15 12 chyba chyba 12 12 chyba
/[/]/; 6 6 7 chyba 6 6 6
jQuery 1.7.1 248 235 138 572 139 171 123 951 136 451 84 197 104 684
jQuery 1.7.1 gzip 72 448 39 626 39 755 40 077 39 650 31 579 36 194

Z porovnávaných nástrojů vychází zdaleka nejlépe Google Closure Compiler. Dokáže správně zpracovat všechny vstupy a poskytuje velikostně nejlepší výsledky. Dokonce i ve whitespace only režimu si vede lépe než JsShrink, protože ve skutečnosti neodstraňuje jen bílé znaky, ale i zbytečné středníky, závorky a podobně.

JSMin a ShrinkSafe si se spoustou vstupů neporadily, jiné vstupy byly schopné dokonce i prodloužit (kvůli přidaným prázdným řádkám na začátek nebo konec souboru). Jedině u jQuery si ShrinkSafe vedl slušně, protože zkracuje i názvy proměnných. Po zagzipování se ale tato výhoda úplně ztratí a kód je opět větší než u JsShrink.

YUI Compressor si u jQuery díky agresivnější minimalizaci proměnných vede dobře, zákeřné vstupy ale zpracovat nedokáže.

Závěr

JsShrink se zaměřuje pouze na vypuštění mezer a komentářů, neprovádí zkrácení názvů proměnných, ani jiná kouzla. Dělá to ale svědomitě, snaží se nevyprodukovat jediný zbytečný bajt a zpracovat všechny platné vstupy.

Zdrojový kód: https://github­.com/vrana/JsShrin­k/

Komentáře

Subscribe
Upozornit na
guest
24 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Pepe

Vím že pro váš způsob využívání to asi není vhodné, ale stejně by mě zajímal vás názor na online nástrojů jako je třeba http://minifyjavascript.com/

blizzz

ja pouzivam vlastny nastroj postaveny nad http://ajaxmin.codeplex.com/ minifier je normalny exac, ktory nacitava cesty z Xml suboru(RIA.pro­ject), kde mam zadefinovane ktore subory a kniznice ma JS projekt obsahovat. Okrem toho ma minifikator podporu externych modulov takze sa automaticky kompiluju subory s priponou coffee do JS atd atd, ma to aj podporu cachovania takze sa kompiluju len tie subory ktore som zmenil, nakoniec mi to vypluje jeden skomprimovany JS subor. Licencia minifieru ma nezaujima pretoze pred nasadenim aplikacie na server exac jednoducho vymazem.

Knyttl

Jedinou nevýhodou JsShrinku je nemožnost zachovat copyright commentáře – nehodí se na slučování 3rd party pluginů do jednoho souboru (což konkurenční nástroje umožňují. Mít tuhle vlastnost, tak je dokonalý – proč mít obrovský ClosureCompiler, když stačí pár řádků, že.

Knyttl

Na syntaxi nesejde, pouze jde o to, aby ta možnost byla :-)

Pepca

Nejen /*! … */, ale prostě první komentář v souboru. Co jsem si všimnul, většina kódů tam ten vykřičník nemá, všichni ale dávají copyright na začátek.

František Kučera

Legrace a software k sobě neodmyslitelně patří – např. vtipné komentáře v kódu, chybové a jiné hlášky, velikonoční vajíčka. Ale licence jsou relativně vážná věc. Ona ta poznámka, že software se smí používat jen pro dobro a ne zlo, zní sice nevinně, ale je velmi nebezpečná – nikdy nevíš, kdy autorovi rupne v bedně a začne se s tebou soudit, že účel, pro který program používáš, není dostatečně dobrý a třeba mu dá i nějaký soudce zapravdu. Je zbytečné a hloupé dávat do licencí takové poznámky a ještě hloupější je vytvářet si na takovém softwaru závislost.

Martin Malý

Pokud si místo „dobro“ a „zlo“ doplníš „otevřený“ a „proprietární“, tak máš GPL jak vyšitou. A máš k tomu i stovky filosofických výkladů, co je přesně v tomto pojetí „dobré“ a co „zlé“. Sám jsi jich několik napsal, že?

Ale v jednom máš pravdu: Je hloupé si na takovém software vytvářet závislosti a po čase třeba zjistit, že o osudu svého SW nerozhoduješ ty sám, ale nějaká komunita a její názor na to, co je „správné“… ;)

František Kučera

Mýlíš se.

fish

a ty mas samozdrejme pravdu, tuhle hlasku vetsina lidi „miluje“, ale to uz jsi zajiste zjistil:)

syntax

Zajímalo by mě porovnání s UglifyJS. I přesto že není příliš známý, dosahuje jen o málo horších výsledků než Closure v podstatně kratším čase a bez magie. UglifyJS používají například Rails (ve výchozí konfiguraci v gemu Uglifier).

Je fakt že oproti JsShrink je budování AST trochu jiný kalibr, každopádně doporučuju se na tento projekt aspoň podívat.

kurkuma

Ja pouzivam Google Closure Compiler nasledovany Dean Edwards packerem.

Myslim, ze by byl mozna dobry i clanek o preprocessorech (na JS a CSS), jestli tady nejaky vubec nekdo pouziva. Ja napr. pouzivam obycejny Ceckovy preprocessor a nemuzu si to vynachvalit. At uz jde o slucovani souboru do jednoho souboru (obyc. #include) a samozrejme makra – jednim parametrem potom napr. odstranim uplne vsechny debugovaci funkce z kodu, takze dany skript je potom mensi a pod. U CSS to same.

Oldis

Ja si upravil Less pro php, a jsmin pro js. preprocesory mimo php nepouzivam z toho duvodu ze mam v configuraci ozanceno co je jen debug, a tedy na ostrem webu se jiz tyto do kompilatu nepridavaji. kompiluje se jen jednou, na vyvojovem/debug servru se kompiluje vzdy, na ostrem jen kdyz chybi pack.

Rekurze

„Hledáte vhodný zmenšovač JS kódu a napadá vás, že by možná nejjednodušší bylo napsat si něco vlastního, než přizpůsobovat svůj toolchain některému z existujících zmenšovačů?“

Ano!

(Proc potom pouziva jsshrink?)

Rekurze

s/pouziva/pouzivat

Michal

Mel jsem s minifikatory problem az do doby nez jsem narazil na GCC. Vetsinou slo o chyby ve starsich IE vzhledem k memu js kodu.
s GCC se mi to za pul roku nestalo a navic pridava x zajimavych veci navic.
Jedna z nich je preklad nazvu promennych tak, ze z nich postupne nadelava a, b, c …. V pripade ze mate promennou lokalni pouze vuci danemu kontextu (napriklad v metode), tak ji prelozi, ostatni nechava tak, aby nenarusil funkcnost.

V pripade ortodoxniho prekladu (volitelna moznost) navic umi prekladat i nazvy trid a dalsi veci, ale k tomu je treba dodrzovat zasady, ktere ja v mem js dodrzene nemam, takze mi vysledny js hazi chyby – pokud bych to vyresil, byl by kod jeste mensi.

Na jednom projektu mam pomerne dost javascriptu, mam ho organizovanej do cca 30 souboru, ktere se pri deploy na produkci v ramci postdeploy tasku bali do jednoho, ten se potom prozene timhle GCC a jeste se hodi do gz, aby si to nemusel resit server pri kazdem pozadavku.
Z puvodnich 30 souboru, ktere maji dohromady cca 1.1MB je asi 400kb gz, coz predcilo vsechna ma ocekavani.
Zjistil jsem ze GCC ty promenne a pripadne dalsi veci preklada tak, aby mely co nejlepsi efekt pri pripadnem dalsim pakovani (co nejmin ruznych pismen atd…).

Pokud byste o tom premysleli, reste to urcite pres lokalni jar jako je to popsano tady https://developers.google.com/closure/compiler/docs/gettingstarted_app .
Online sluzby maji limity myslim nekde kolem 20-30 prelozenych souboru za hodinu, posilat to pokazde na sluzbu o ktere navic nevite jestli bude zrovna ready se mi zda dost zbytecny a nesmyslny.

Nebojte se toho, pracuje se s tim krasne a je to spolehlive.
Doporucuju vsem.

Michal

Pardon, 400kb mel vysledek bez gzipu, s ma 156kb :-)

Aichi

Posilat ruzne GZipovany JS je prasecina. Jaky duvod mas k tomu, aby to nedelal za tebe server? Mas ho pretizeny? Je lepsi to nechat na serveru z toho duvodu, ze soubor se nakesuje v prohlizeci a pak se uz stahovat nemusi – usetris na prenosu tak jako tak, ale po kazdem pristupu se musi v pameti pomoci JS rozbalit a to zdrzuje daleko vic.

Michal

Nevim co resis. V mem pripade si browser rekne GET blabla.js, server se mrkne na umisteni, zjisti, ze vedle blabla.js ma polozenej blabla.js.gz, tak se neobtezuje s packovanim za letu a vrati ten pripravenej. Datum modifikace se nemeni. Na to je na nginx takovej plugin..

bckp

Doporučuju se podívat taky na online utilitku napsanou celou v JS, používám ji již nějaký ten pátek a příjde mi, že její výsledky předčí vše ostatní, navíc jako jedna z mála nemá problém minimalizovat i prototype tak, aby fungoval…

http://jsutility.pjoneil.net/

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.

Pocta C64

Za prvopočátek své programátorské kariéry vděčím počítači Commodore 64. Tehdy jsem genialitu návrhu nemohl docenit. Dnes dokážu lehce nahlédnout pod pokličku. Chtěl bych se o to s vámi podělit a vzdát mu hold.