Polub bloga na fejsie!

Konfiguracja Webpack 2+ – część #3: pluginy

4

Za nami już dwie części mini-serii na temat konfigurowania nowej wersji webpacka. Dotychczas dowiedzieliśmy się co nieco na temat podstawowej konfiguracji plików wejściowych oraz wyjściowych. Z ostatniego wpisu natomiast, wiemy już czym są loadery i jak możemy je wykorzystać. Dziś przyszła pora na pokazanie trzeciej, istotnej przy konfiguracji webpacka rzeczy, którą są pluginy. Myślę, że nie ma co przedłużać – zapraszam do lektury!

Czym są pluginy w webpacku?

Pluginy, czyli po naszemu wtyczki, możemy spotkać w wielu systemach i aplikacjach. Zwykle jest to zewnętrzny kod, nie koniecznie stworzony przez twórców danego rozwiązania, który dodaje jakieś nowe funkcjonalności lub modyfikuje działanie danej aplikacji. Podobnie jest w przypadku webpacka – pluginy pozwalają „customizować” proces budowania „bundli” na wiele różnych sposobów.

Webpack posiada wiele wbudowanych, przydatnych pluginów, których kilka przykładów pokażę za moment. Istnieją też oczywiście wtyczki stworzone przez innych, nie związanych z webpackiem programistów, które dostępne są w repozytorium npm – niektóre z nich również za chwilę przedstawię.

Jeśli chodzi o konfigurację, to sprowadza się ona do dodania danego pluginu do tablicy, którą przypisujemy do właściwości plugins obiektu konfiguracyjnego webpacka. Zresztą spójrz na poniższy przykład:

W powyższym, przykładowym kodzie widać, że tablica plugins zawiera wbudowany w webpacka plugin UglifyJsPlugin. Zwróć uwagę na użycie słowa kluczowego new – w ten sposób przekazujemy do webpacka instancję obiektu wtyczki. Konstruktory pluginów często przyjmują też jako parametr obiekt, w którym przekazujemy do niego jakąś dodatkową konfigurację. Warto więc dobrze przeczytać dokumentację pluginu aby wiedzieć jak w pełni wykorzystać jego możliwości!

Pod tym adresem znajdziesz listę wszystkich wbudowanych w webpack pluginów, wraz z ich opisem i konfiguracją. A tutaj kilka zewnętrzych wtyczek. Poniżej przedstawię kilka najczęściej stosowanych pluginów: zarówno tych wbudowanych jak i zewnętrznych.

Przykłady

Tak jak obiecałem – teraz czas na przykłady najbardziej przydatnych pluginów jakie często wykorzystuje się w projektach! Przykłady te będę przedstawiać „przyrostowo”, tzn. kolejne listingi będą zawierały kod z poprzedniego przykładu. Dzięki temu na koniec wpisu będziesz mógł zobaczyć jak wygląda pełna konfiguracja wszystkich wtyczek na raz.

CommonsChunkPlugin

Jest to bardzo przydatna wtyczka, która potrafi spośród wszystkich modułów używanych przez poszczególne punkty wejściowe aplikacji, wybrać te, które są przez nie współdzielone. Następnie wszystkie te współdzielone moduły potrafi „wypluć” do dodatkowego, osobnego „bundla”, który może zostać załączony na każdej podstronie aplikacji jako wymagany przez wszystkie „bundle”.

Konfiguracja tego pluginu jest dość prosta:

Wtyczka CommonsChunkPlugin jest, jak widać, pluginem wbudowanym w webpacka. Powyższy przykład przedstawia jego prostą konfigurację, która będzie skutkować tym, że ten dodatkowy, wynikowy „bundle” ze współdzielonymi modułami będzie miał nazwę vendor.bundle.js. Warto wiedzieć, że ciąg znaków przypisanych do właściwości filename może zawierać te same „placeholdery” co w przypadku właściwości filename obiektu output, o której pisałem w pierwszym wpisie tej serii.

Plugin ten ma trochę więcej opcji konfiguracji, warto więc się z nimi zaznajomić aby poznać pełnię możliwości tego pluginu.

UglifyJsPlugin

Kolejny plugin będący częścią webpacka – posiada też jednak wersję dostępną poprzez repozytorium npm. Myślę, że jego nazwa od razu przywodzi na myśl do czego może on służyć… Jest to bowiem wtyczka, która pozwala dokonać optymalizacji i minifikacji wynikowego kodu „wypluwanego” przez webpack, a która do tego celu wykorzystuje popularne narzędzie UglifyJS.

Spójrz na przykładową konfigurację webpacka z omawianym pluginem:

Obiekt konfiguracyjny przekazywany do konstruktora wtyczki UglifyJsPlugin pozwala na konfigurację wszystkich opcji jakie dostępne są w narzędziu UglifyJS.

POLUB BLOGA NA FACEBOOKU!

Chcesz być na bieżąco informowany o nowościach na blogu oraz innych ciekawych treściach? Polub fanpage bloga na Facebooku!

Uwaga 1: Plugin ten wymaga do działania osobnej instalacji narzędzia UglifyJS, dostępnego standardowo w npm.

Uwaga 2: W momencie pisania tego tekstu, istnieją znane problemy z minifikacją kodu wynikowego w ES6. Jest na to rozwiązanie wymagające użycia pluginu w wersji dostępnej jako osobna paczka npm. Więcej znajdziesz w dokumentacji.

ExtractTextPlugin

O wtyczce tej wspominałem już przy okazji poprzednich części tej serii. Pozwala ona na wycięcie tekstu z „bundla” i wrzucenie do osobnego pliku wynikowego. Najczęściej wykorzystuje się ją do wyciągania kodu CSS do osobnych „bundli”.

Aby skonfigurować ją do tego właśnie celu nie wystarczy dodać wywołania konstruktora do tablicy plugins. Należy jeszcze odpowiednio zmodyfikować „loader” odpowiedzialny za obsługę plików CSS – ale o tym za moment…

…ponieważ najpierw plugin ten trzeba zainstalować (nie jest on wbudowany w webpacka):

Kiedy wtyczka jest już zainstalowana, można już jej użyć do odpowiedniego wydzielania kodu CSS do osobnych plików:

Zanim przejdę do omówienia konfiguracji, zwróć uwagę na import obiektu ExtractTextPlugin z pakietu, który przed momentem zainstalowaliśmy.

Omówienie konfiguracji

W przykładzie pokazałem użycie pluginu ExtractTextPlugin zarówno dla plików CSS jak i Sass. Zwróć uwagę jak, w stosunku do tego co pokazałem w poprzednim wpisie, zmodyfikowany został kod konfiguracji loaderów. Do właściwości use przypisano wywołanie funkcji ExtractTextPlugin.extract(). Przekazano do niej obiekt z opcjami, który zawiera dwie właściwości: fallback oraz use.

Pierwsza z tych właściwości służy do skonfigurowania dodatkowego loadera dla kodu CSS, który nie został wyekstrahowany. To dlatego, że standardowo wtyczka wycina kod CSS tylko z głównych „bundli”. Pomija natomiast dodatkowe, stworzone na przykład przez plugin CommonsChunkPlugin). Zwykle jest to porządane zachowanie, ponieważ „bundle” generowane przez tę wtyczkę to zwykle moduły zaimportowane z node_modules.

Druga opcja konfiguracyjna funkcji ExtractTextPlugin.extract to już konfiguracja odpowiednich loaderów, mających odpowiednio zająć się kodem CSS zaimportowanym jako moduły JavaScript.

Na koniec spójrz jeszcze na tablicę plugins. Do konstruktora ExtractTextPlugin przekazuję obiekt zawierający jedną właściwość: filename. Służy ona do sterowania nazwą generowanego pliku CSS. Jak widzisz, użyłem w niej „placeholdera” [name]. Dzięki temu dla każdego z „bundli” będzie wyekstrahowany osobny plik CSS (np. dla home.bundle.js powstanie też plik home.bundle.css itd.).

Wtyczka ma dość spore możliwości konfiguracji, warto więc samemu przeanalizować dostępne opcje, o których więcej przeczytasz w dokumentacji.

HtmlWebpackPlugin

Mamy już omówione trzy popularne pluginy. Nadszedł więc czas na ostatni, jaki dziś omówię… a jest nim wtyczka HtmlWebpackPlugin. Służy ona do generowania pliku index.html, w którym automatycznie załączane są odpowiednie „bundle” (zarówno JS jak i CSS). Jest to narzędzie przydatne szczególnie przy tworzeniu aplikacji typu SPA.

Wtyczka ta również nie jest wbudowana w webpacka, wymagana jest więc najpierw jej instalacja:

Gdy mamy ją już zainstalowaną, można przejść do jej konfiguracji – w najprostszym przypadku nie ma tutaj wielkiej filozofii:

Oczywiście, z racji tego, że jest to osobny pakiet, musimy go najpierw zaimportować do zmiennej.

Jedyne co musimy zrobić później, to dodanie wywołania konstruktora obiektu HtmlWebpackPlugin. Jeśli nie przekażemy mu obiektu konfiguracyjnego, to po uruchomieniu komendy webpack w konsoli, w katalogu docelowym pojawi się plik index.html, wyglądający mniej więcej tak:

Jak widzisz, na końcu sekcji body załączone zostały wszystkie wynikowe „bundle” (łącznie z vendor.bundle.js wygenerowanym przez plugin CommonsChunkPlugin). Oprócz tego, w sekcji head załączony został plik home.bundle.css wygenerowany przez wtyczkę ExtractTextPlugin.

Oczywiście możliwe jest też wygenerowanie wielu wynikowych plików HTML, na przykład osobnych dla każdego z „bundli”. Po więcej informacji odsyłam do dokumentacji tej wtyczki!

Pluginy webpack – podsumowanie

W ten sposób dobrnęliśmy do końca tego wpisu – mam nadzieję, że pluginy webpacka nie będą już dla Ciebie tajemnicą… Ogólnie można powiedzieć, że wraz z loaderami stanowią one trzon każdej konfiguracji. Warto więc wiedzieć jak z nich korzystać.

Tak sobie myślę, że pojawi się jeszcze co najmniej jedna część serii na temat podstaw konfiguracji webpacka! Postaram się opisać w niej strategie dotyczące konfiguracji deweloperskiej i produkcyjnej. Wydaje mi się, że warto by również było omówić wbudowany w webpacka lokalny serwer deweloperski. Nie wspominając juz o takim temacie jak „hot realoading”! Pytanie tylko czy zmieszczę wszystko jednym wpisie czy rozbiję temat na więcej artykułów?

P.S. Myślę, że czas już udostępnić link do repozytorium GitHub [klik], w którym znajdziesz omówione dotychczas przykłady.


Jako, że powyższy wpis jest częścią większej całości, poniżej podaję linki do wszystkich odcinków serii na temat podstaw konfiguracji webpacka 2+:

REACT, REDUX, REACT-ROUTER - KURSY ON-LINE

Chcesz od podstaw poznać tajniki React, Redux oraz react-router? Zapraszam do moich szkoleń on-line:

Przejdź do szkoleń

Uwaga! Obecnie trwa przedsprzedaż kursów - premiera 1 sierpnia 2017!

  • Jarema Antosz

    Jak modyfikujesz konfiguracje pluginów w zależności od środowiska? UglifyJSPlugin bardzo spowalnia mi build na dev. Ja zrobiłem to tak:
    W sekcji scripts w package.json:
    „scripts”: {
    „start”: „webpack-dev-server –env.target=development”,
    „build”: „webpack –env.target=production”,
    „test”: „echo „test script””
    }
    W webpack.config.js napisałem funkcje, która zwraca pluginy w zależności od docelowego środowiska:
    function getPlugins(target) {
    const plugins = [
    new HtmlWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin(), //auto refresh app in browser after file save but state is not loss, requires react-hmre
    new webpack.NamedModulesPlugin(), // prints more readable module names in the browser console on HMR updates
    new webpack.NoEmitOnErrorsPlugin(),
    new CleanWebpackPlugin(PATHS.build)
    ];

    if(target === ‚production’) {
    plugins.push(new webpack.optimize.UglifyJsPlugin({ sourceMap: true, minimize: true, compress: true }));
    }

    return plugins;
    }

    I użyłem jej w module.exports:
    module.exports = function(env) {
    return {

    plugins: getPlugins(env.target)

    }
    };

    Czy taka konfiguracja jest ok? Jak byś to zrobił po swojemu?

    • tak jak wspomniałem na końcu powyższego wpisu, planuję więcej o tym napisać w kolejnym poście… 😉
      natomiast jeśli chodzi o Twoje podejście to dobrze kombinujesz! 😉 dużo zależy od wielkości konfiguracji oraz tego jak bardzo się różnią od siebie konfiguracje dla prod i dev – fajnym podejściem jest zrobienie konfiguracji „default”, która zawiera tylko to co jest wspólne dla obu opcji, a do tego dodać dwie konfiguracje dla prod i dev – później wystarczy zmerge’ować te dwa obiekty w zależności od zmiennej środowiskowej
      żeby jednak nie było – podejście z funkcjami zwracającymi odpowiednie kawałki konfiguracji też jest ok

      • Jarema Antosz

        Czy w praktyce te różnice są bardzo duże?

        • różnice między dev i prod? różnie, w zależności od wielkości projektu – im projekt większy tym więcej rzeczy może spowalniać builda developerskiego więc włącza sie je tylko dla proda lub używa innych ustawień – zwykle większość loaderów trafia do default, a pluginy raczej rozdzielone (chociażby hotreload dla deva, UglifyJS dla proda itp), podobnie jak input/output (dla deva zwykle mamy lokalny devserwer) – do tego różne ustawienia dla sourcemap itp. – jak się zacznie konfigurować to się może zrobić tego sporo 😉

Google Analytics Alternative