Publikoval Michal Kočí dňa 9.8.2015 o 11:00 v kategóriach Gulp.js a Optimalizácia webu
V prvej časti miniseriálu sme si pripravili jednoduchú stránku, v druhej sme si nainštalovali Gulp.js a naprogramovali prvý task. Dnes si doprogramujeme ďalšie tasky, ktorými budeme optimalizovať náš web. Budeme minifikovať, kombinovať a verzionovať štýly a skripty.
Aby ste zrýchlili jeho načítanie v prehliadač a tak vašim návštevníkom spríjemnili návštevu.
Pokiaľ je váš návštevník na rýchlom pripojení, je možné, že vašu optimalizáciu ani nepostrehne, najmä pri malom webe. Ale potom sa k vám vráti cez mobil, pripojený cez 3G a bude niekoľko sekúnd tupo zírať na prázdnu stránku. A to predsa nechcete.
Chceli by ste programovať optimalizované weby? Dojdite na jedno z mojich školení JavaScriptu (Node.js, jQuery, AngularJS, ...), na nich sa okrem iného naučíte, ako naprogramovaný web optimalizovať tak, aby z neho vaši používatelia mali radosť.
Treba si uvedomiť, že spomalenie vzniká v dôsledku:
Aké sú najčastejšie problémy webu z pohľadu rýchlosti sme si vysvetlili v predchádzajúcom odstavci. Ak navyše váš web neindikuje (cez HTTP hlavičky), že sa tieto súbory môžu v prehliadači kešovať, bude si ich prehliadač sťahovať pri každej návšteve stránky. Pri každej.
Čo s tým môžete robiť?
Na serveri optimalizujete najčastejšie takto:
Ak máte serverové nastavenia poriešené (alebo viete, že budete mať), optimalizovať môžete (mali by ste) aj samotnú aplikáciu:
V prípade že spojíte súbory (napríklad štýly) do jedného a povolíte ich kešovanie, pri ich zmene si ich prehliadač štandardne nenačíta znova (lebo ich má nakešované). Aby ste prehliadač prinútili si ich znovu stiahnuť, musíte vyvolať zmenu URL (názvu súboru alebo query string).
Na toto sa používa verzovanie. Jedno z riešení, ktoré použijeme aj my je, že po spojení súborov do jedného sa z jeho obsahu vypočíta tzv. checksum, teda nám vznikne akýsi reťazec, ktorý závisí od obsahu súboru. V HTML potom linkujete tento súbor a jeho názov a obsah si prehliadač nakešuje.
Keď vykonáte zmeny a znovu sa spoja súbory do jedného, vypočítaný checksum sa bude od toho predchádzajúceho líšiť. V HTML linkujete na tento nový, ktorý pri prvej návšeteve prehliadač nebude mať ešte nakešovaný a tak si ho stiahne (a zasa nakešuje) a pri ďalšej už bude súbor čítať z keše.
Je to jednoduché a veľmi efektívne riešenie. A dá sa ľahko automatizovať, čo si dnes aj ukážeme.
Keďže budeme Gulpom niektoré súbory modifikovať, iné vytvárať, nie je vhodné, aby sme menili naše pôvodné a zdrojové súbory. Preto sa obvykle výstup z automatizácie zapisuje do iného adresára. My použijeme adresár dist, ktorý bude v roote nášho projektu. Vy si však zvoľte názov a jeho polohu tak ako chcete.
V adresári dist budeme mať podobnú štruktúru, ako máme v zdrojovom adresári, teda budeme mať podadresáre js, css a font.
Poďme teda na naše úlohy.
Ako prvé si spojíme linkované súbory. Ak si pozrieme štýly, ktoré linkujeme, uvidíme v HTML toto:
<link rel="stylesheet" href="css/reset.css"> <link rel="stylesheet" href="css/material.css"> <link rel="stylesheet" href="css/weather-icons.css"> <link rel="stylesheet" href="css/weather.css"> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" type="text/css">
To je spolu 6 súborov štýlov. Posledné dva sú pre webové fonty, tie chceme naďalej načítavať z webu a tak tie necháme tak a budeme spájať iba prvé 4.
Použijeme skvelý npm balíček (modul) gulp-useref, ktorý však od nás vyžaduje drobnú pomoc. Musíme mu dať vedieť ktoré štýly a skripty chceme spojiť a do akého súboru. To sa robí pridaním komentárov do HTML.
Začnime tým, že si nainštalujeme balíček (ako čisto vývojový):
npm install --save-dev gulp-useref
Prejdeme do editora, kde máme otvorený gulpfile.js
a v ňom si načítame tento modul, niekde na začiatku skriptu:
var useref = require('gulp-useref');
A vytvoríme si zatiaľ prázdny task s názvom production:
gulp.task('production', function() { });
Náš task bude pracovať nad hlavným dokumentom. Z neho si načíta, ktoré súbory má spojiť, spojí ich, zapíše ich na disk a potom zapíše na disk upravený HTML dokument, ktorý bude namiesto niekoľkých štýlov a skriptov odkazovať na ich spojené verzie.
Upravme si teda HTML a obaľme štýly a skripty patričnými komentármi. Výstupný skombinovaný súbor chceme aby sa volal weather.css
resp. weather.js
:
<!-- build:css css/weather.css --> <link rel="stylesheet" href="css/reset.css"> <link rel="stylesheet" href="css/material.css"> <link rel="stylesheet" href="css/weather-icons.css"> <link rel="stylesheet" href="css/weather.css"> <!-- endbuild -->
A pre skripty (na konci stránky):
<!-- build:js js/weather.js --> <script src="js/jquery-2.1.4.js"></script> <script src="js/material.js"></script> <script src="js/weather.js"></script> <!-- endbuild -->
Upravme si teda úlohu, začneme jednoducho a postupne budeme pridávať funkcionalitu.
Plugin useref
má metódu assets
, ktorá keď sa zapojí do pipeline, tak preskenuje vstupné súbory (v našom prípade jediný HTML súbor) a nájde si svoje komentáre.
Vytvorí z nich nový stream, s ktorým ďalej viete pracovať (pridať minifikáciu či verzovanie) a keď ste hotový, zavoláte nad týmto streamom funkciu restore
, ktorá vám do hlavnej pipeline vráti pôvodné súbory (naše HTML).
Samotná funkcia useref
vám potom HTML zmodifikuje a všetko čo je medzi komentármi vám nahradí odkazom na skombinovaný súbor.
Prvá verzia našej úlohy tak bude vyzerať nasledovne:
gulp.task('production', function() { var assetsStream = useref.assets(); return gulp.src('index.html') .pipe(assetsStream) .pipe(assetsStream.restore()) .pipe(useref()) .pipe(gulp.dest('dist')); });
Stream, ktorý nám vracia funkcia assets
si uložíme do premennej, aby sme nad ním neskôr vedeli zavolať restore
.
Čiže, začíname tým, že si načítame náš HTML súbor a dáme ho do pipiline (gulp.src
). Hneď v zápätí do pipeline strčíme assets
- plugin do neho pridá skombinované súbory. Pridaním restore
sa vrátime k pôvodnému HTML a volaním useref
sa v ňom nahradí odkaz na skripty a štýly. Výsledok zapíšeme do adresára dist (cez gulp.dest
).
Po spustení sa nám toho až tak veľa užitočného nestalo, ale sú to solídne základy, na ktorých môžeme stavať. V adresári dist nám vznikol index.htm čo je skoro kópia nášho pôvodného súboru s tým rozdielom, že v ňom už nájdeme odkazy na kombinované štýly:
<link rel="stylesheet" href="css/weather.css"> <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" type="text/css">
A na kombinované skripty:
<script src="js/weather.js"></script>
Kde sú skombinované súbory? Tam kde majú byť, v patričných podadresároch (css a js). My ich ovšem chceme nie len spojené, ale hlavne minifikované a verzionované. A tie si čochvíľa zabezpečíme.
Fajn, v istom kroku náš stream obsahuje všetky kombinované skripty a štýly. V našom prípade jeden kombinovaný štýl a jeden skript, ale v reále si ich môžete nechať vygenerovať viacero ak chcete.
To je ten správny moment na ich minifikáciu. Na minifikáciu slúžia rôzne algoritmy a rôzne moduly. Na minifikáciu skriptov použijeme modul gulp-minify-css a na minifikáciu skriptov gulp-uglify.
Treba si však uvedomiť, že v jednom momente máme v streame rôzne typy súborov - skripty a štýly. A na rôzne súbory chceme použiť rôzne pluginy. A presne s týmto nám pomôže modul gulp-if.
Všetky tri si nainštalujeme:
npm install --save-dev gulp-minify-css npm install --save-dev gulp-uglify npm install --save-dev gulp-if
A na všetky tri si načítame cez require:
var minify = require('gulp-minify-css'); var uglify = require('gulp-uglify'); var gulpif = require('gulp-if');
A nakoniec si ich zapojíme do pipeline. Kde? Ešte pred volanie restore
. Aktuálna verzia tasku tak vyzerá nasledovne:
gulp.task('production', function() { var assetsStream = useref.assets(); return gulp.src('index.html') .pipe(assetsStream) .pipe(gulpif('*.css', minify())) .pipe(gulpif('*.js', uglify())) .pipe(assetsStream.restore()) .pipe(useref()) .pipe(gulp.dest('dist')); });
Do pipeline pridávame dve veci. V prvom kroku použijeme gulp-if
na volanie modulu gulp-minify-css
, pričom vravíme, nech sa spustí aby na css súbory. V druhom potom gulp-uglify
na js súbory.
Po spustení tasku vidíme, že vygenerované súbory (weather.css a weather.js) sú už minifikované. Ale ešte potrebujeme zabezpečiť verzionovanie.
Samotné verzionovanie potrebujeme vykonať v dvoch krokoch. Najskôr potrebujeme súbory premenovať, t.j. pridať do nic reťazec vypočítaný na základe obsahu. Na to nám výborne poslúži modul gulp-rev.
Spájanie súborov? Minifikácia? Verzionovanie? Áno, aj im sa venujeme na mojich školeniach JavaScriptu (Node.js, jQuery, AngularJS, ...). Nakódiť web je jedna vec, ale naprogramovať ho tak, aby sa aj rýchlo načítaval, je nemenej dôležité.
To však nestačí. Je síce fajn, že sa názov súboru upraví, ale ešte potrebujeme, aby sa zo samotného HTML dokumentu linkoval ten správny súbor, teda ten s verziou v názve. Na to zase použijeme modul gulp-rev-replace.
Nainštalujeme si obidva:
npm install --save-dev gulp-rev npm install --save-dev gulp-rev-replace
A načítame si ich na začiatku skriptu:
var rev = require('gulp-rev'); var revReplace = require('gulp-rev-replace');
A stačí ich zapojiť na správne miesta v pipeline a budeme hotový. Kde ich zapojíme? Každý inde. Prvý ešte pred restore, lebo musí pracovať so spojenými a minifikovanými css a js súbormi. Druhý ale až za restore, lebo ten pracuje práve s HTML súborom.
Naša ďalšia verzia tasku vyzerá nasledovne:
gulp.task('production', function() { var assetsStream = useref.assets(); return gulp.src('index.html') .pipe(assetsStream) .pipe(gulpif('*.js', uglify())) .pipe(gulpif('*.css', minify())) .pipe(rev()) .pipe(assetsStream.restore()) .pipe(useref()) .pipe(revReplace()) .pipe(gulp.dest('dist')); });
No vidíte, ako sa nám to pekne poskladalo. V adresári dist teraz máme html súbor, štýly aj skripty. Ale chýbajú nám tam ešte fonty.
Ak sa v tomto momente pozriete do výstupného adresára (dist), tak si všimnete, že nám tam chýba adresár s fontami. Rovnako ak si z tohto adresára otvoríte index.htm
v prehliadači tak uvidíte, že na stránke sa nezobrazuje ikonka počasia (pochádzajúca z webového fontu).
Musíme teda do adresára dist dostať ešte fonty a keďže s nimi nechceme robiť žiadnu optimalizáciu ani konverziu, task bude veľmi jednoduchý:
gulp.task('fonts', function() { return gulp.src('./font/*') .pipe(gulp.dest('dist/font')); });
Veľmi jednoduchá úloha, však? Načítame všetky súbory z adresára font a skopírujeme ich do adresára dist.
Teraz stačí spustiť úlohu fonts a potom production a máme v adresári dist kompletný a funkčný výsledok. Aktuálnu verziu riešenia nájdete v adresári 03 na mojom githube.
Ale ešte nie sme na konci. Nabudúce sa pozrieme na to, ako spúšťať viacero úloh v stanovenom poradí. Takže ostaňte naladení...
Ak nechceš premeškať príspevky ako je tento, sleduj ma na Twitteri, alebo ak máš RSS čítačku, môžeš sledovať môj RSS kanál.