Rzut oka na Gulp.js

Ostatnio nie bardzo mam czas na życie - najpierw podyplomówka, potem duży projekt dla pewnego dystrybutora foteli masujących, a na głowie jeszcze masa pomniejszych tematów. Przez to wszystko nawet jak znajdę nieco wolnego, to wolę się odmóżdżyć i w coś pograć, a nie jeszcze się produkować jako bloger. Ale na szczęście czasem znajduję trochę siły na programowanie dla przyjemności. Chwilowo mam na tapecie dwa projekty. Przy okazji pisania dokumentacji jednego z nich postanowiłem wypróbować coś nowego i wymieniłem wysłużonego Grunta na konkurencyjny task runner, czyli Gulp.

Pierwszy z projektów nich to skrypt służący do budowania slidera 360° (to akurat na potrzeby wspomnianych foteli - chodzi o możliwość obracania produktu i oglądania go pod dowolnym kątem), drugi to następstwo rychłego przejścia Bootstrapa na Sass, preprocesor, którego organicznie nie trawię. Zmiana preprocesora jest odtrąbiana jako wielki sukces, a dla mnie to coś, co pogrzebuje Bootstrapa głęboko pod ziemią. A że alternatywy są takie sobie, postanowiłem odkurzyć i odgrzać (a w zasadzue to przepisać) uStyle, czyli mojego autorskiego frameworka opartego o Stylusa.

Sam Stylus zasługuje pewnie na oddzielny wpis, bo technologia to mocno ciekawa (i niepozbawiona swoich dziwactw w stosunku do o wiele dojrzalszego, przynajmniej w moim odczuciu, LESS). Jednak chciałem naskrobać dwa słowa o czym innym. Otóż przy okazji pisania dokumentacji do nowego uStyle postanowiłem wypróbować coś nowego i wymieniłem wysłużonego Grunta na konkurencyjny task runner, czyli Gulp.

Jeśli ktoś nie kojarzy, co to takiego Grunt, czy Gulp, to szybko wyjaśniam. Zadaniem obu narzędzi jest zautomatyzowanie pewnych procesów, które składają się na budowanie projektu: przemielenie plików LESS (albo Stylus) i wyplucie gotowego CSS, połączenie i zminifikowanie skryptów JS, obserwowanie zmian w plikach i odpowiednie reagowanie na wprowadzanie zmiany, sprawdzanie poprawności stylu kodowania za pomocą narzędzi takich jak JSCS, czy JSHint i wiele innych.

Grunt oferuje podejście skoncentrowane na zadaniach. Zadanie "stylus" będzie zatem czytało źródłowy plik lub pliki, zapisując wynik jako plik wyjściowy o podanej nazwie. Przygotowanie plików JS wymagało będzie dwóch zadań - najpierw "concat" połączy ze sobą wiele plików JS, po czym zapisze wynik jako plik wyjściowy. Zadanie "uglify" zrobi dokładnie to samo, ale po drodze jeszcze dokona minifikacji (obfuskacji) kodu. Krótko mówiąc, Grunt oferuje gotowe zadania, które przy minimalnej konfiguracji wykonują po ileś tam czynności, nieraz duplikujących się, w celu osiągnięcia pożądanego wyniku.

Gulp ma inne podejście. Zadania nie są oferowane jako gotowe do pobrania paczki, którym tylko trzeba podać jakąś, na ogół minimalistyczną, konfigurację. Zamiast tego, zadanie są tworzone przez programistę z paczuszek, które mają znacznie mniejszy zakres odpowiedzialności. Kolejne kroki każdego zadania są łączone w strumień. Najpierw informujemy Gulpa, jakie pliki wejściowe nas interesują, następnie przetwarzamy je odpowiednim pluginem lub pluginami, dalej zupełnie innym pluginem nadajemy nazwę plikowi wyjściowemu, a na koniec informujemy Gulpa. gdzie chcemy, by zapisał plik wyjściowy. Minifikacja nie zajmuje się konkatenacją, a kod preprocesora CSS zajmuje się tylko i wyłącznie wygenerowaniem kodu CSS, a nie zapisaniem pliku, czy nadaniem mu nazwy. Dzięki takiemu rozdrobnieniu, tworzone zadania mogą być bardziej przekrojowe, a to, co w Gruncie nieraz należało zawrzeć w oddzielnych zadaniach, w przypadku Gulpa może tworzyć pojedyncze zadanie.

Przykładem mógłby być tutaj preprocesor Stylusa połączony z autoprefixerem. Grunt zmusiłby nas do użycia Stylusa do wyprodukowania danych pośrednich i zapisania ich w pliku tymczasowym, a następnie, w oddzielnym zadaniu, uruchomienia autoprefixera na danych pośrednich w celu wyplucia końcowych plików. Do tego doszłoby zapewne kolejne zadanie, które za nas wyczyści katalog z danymi pośrednimi. Gulp za to pozwala łączyć poszczególne kroki w jeden strumień, przez co unikamy zapisywania danych pośrednich. Wyjście z jednego modułu jest przekazywane do kolejnego bez zapisywania czegokolwiek na dysku. To również sprawia, że Gulp jest szybszy niż Grunt (czasem tylko trochę, a czasem wielokrotnie szybszy - zależy to od zadania).

Dodatkową ciekawą rzeczą jest fakt, że Gulp nie bawi się w konfigurację, a pozwala na bezpośrednie wykorzystanie modułów wykonujących interesująće nas czynności (code over configuration). Wszystko dzieje się w kodzie i jest pod ścisłą kontrolą. Jeśli coś wymaga konfiguracji, to wywołanie danego modułu po prostu może taką konfigurację przyjąć.

Przykładowe zadanie Gulpa może być zapisane następująco:

var gulp = require("gulp");
var stylus = require("gulp-stylus");
var autoprefixer = require("gulp-autoprefixer")
var rename = require("gulp-rename");

gulp.task("css", function() {
    gulp.src("app/styl/style.styl")
        .pipe(stylus({"import css": true}))
        .pipe(autoprefixer("last 1 version", "> 1%", "ie 8", "ie 7"))
        .pipe(rename("style.css"))
        .pipe(gulp.dest("build"));
});

Widać tu dokładnie podejście, o którym pisałem wyżej. W pliku definiującym zadanie "css" (swoją drogą, kontrola nad nazwą zadania sprawia, że wreszcie można nazwać je tak, by faktycznie mówiło, czym się zajmuje) pobieramy sobie moduły odpowiedzialne za przetwarzanie plików Stylusa, za automatyczne prefiksowanie reguł oraz za zmienianie nazw plików. Następnie w zadaniu definiujemy kolejne kroki: karmimy Gulpa plikami wejściowymi, przetwarzamy je, ustalamy nazwę wynikowego pliku (w tym przypadku jest ona identyczna z domyślną, więc ten krok można było pominąć, ale wystarczyłoby nazwać plik Stylusa index.styl, a już zmiana nazwy zaczęłaby mieć sens), a na koniec prosimy Gulpa o zapisanie wyniku w katalogu build. Pięć kroków, połączonych w jeden strumień. Między krokami nie są zapisywane żadne pliki pośrednie, a całość wykonuje się w całości w pamięci, co, jak już wspomniałem wcześniej, mocno przyspiesza cały proces.

Mam jeszcze wiele do poznania w światku Gulpa. Wiem z lektury dokumentacji jednego z modułów, że możliwe jest obserwowanie zmian w plikach i reagowanie nie na cały ich zestaw, a na ten konkretny plik, który się zmienił. Nie wiem natomiast, czy wszystkie zadania, do których wykorzystywałem Grunta, będą dostępne. Pamiętajmy, że Gulp dysponuje znacznie mniejszą liczbą modułów i jest młodszym graczem niż Grunt. Myślę jednak, że z czasem dokopię się do wszystkiego, co chcę osiągnąć, a jak czegoś mi zabraknie, to po prostu napiszę odpowiedni plugin.

Czy będę się przerzucał z Grunta? Na pewno nie będzie mi się chciało przenosić już istniejących projektów. Zresztą nie sądzę, żeby miało to większy sens. W przypadku nowych projektów prawdopodobnie jeszcze się Gulpem pobawię. Wygląda naprawdę obiecująco.


Komentarze

JarekMac

2015-11-24, 10:05

Grunt jest o tyle fajny, że ma świetne pluginy jak SASS, LESS, czy WATCH, do których człowiek szybko się przyzwyczaja. Artykuł bardzo ciekawy, może kiedyś z ciekawości wypróbuje Gulpa ;) Pozdrawiam!

Dominik Marczuk

2015-11-24, 10:52

Dzięki! Faktycznie, do Grunta łatwo sie przyzwyczaić, ale pod tym względem Gulp mu nie ustępuje. Pluginów jest może mniej, ale te najczęściej używane są równie wysokiej jakości. Brałem ostatnio udział w hackathonie organizowanym przez Sii i wykorzystaliśmy na froncie właśnie Gulpa. Spisał się super :).


Napisz komentarz


Szukaj wpisów


Chmura tagów