W poprzedniem wpisie na temat konfiguracji webpacka 2 opisałem jak skonfigurować punkty wejściowe oraz miejsce docelowe dla generowanych “bundli”. Dziś przyszedł czas na ciąg dalszy, w którym porozmawiamy o najważniejszej części każdej webpackowej konfiguracji jaką są loadery! W ramach przykładów postaramy się skonfigurować kilka najważniejszych i najczęściej używanych loaderów. Zapraszam do lektury!

Czym są loadery?

Jak wiemy z poprzedniej części, webpack potrafi zbudować drzewo zależności modułów JavaScript, odpowiednio je połączyć, a na koniec “wypluć” odpowiednią paczkę. Jednak “z pudełka” potrafi on tylko ładować moduły w stylu Node.js (eksportowane za pomocą module.exports i ładowane za pomocą require()). Na szczęście został on tak zaprojektowany aby można było łatwo rozszerzać go o umiejętność traktowania jako moduły również innych rzeczy. Dla przykładu możliwe jest ładowanie w modułach JavaScript plików CSS (jak i LESS/SCSS), plików JSON, obrazków itd.

Wszystko to możliwe jest dzięki tzw. “loaderom modułów”. Loadery takie to zewnętrzne biblioteczki, które rozszerzają możliwości webpacka o umiejętność, między innymi, ładowania plików innych typów niż JavaScript. Dostarczają też możliwości pre-procesowania plików podczas ich ładowania w module (oraz przeprowadzania w zasadzie dowolnych operacji na ładowanych modułach). Dzięki temu możemy ładować pliki napisane w TypeScript/ES6, które “w locie” zostaną przetłumaczone na JavaScript (zrozumiały dla przeglądarek). To samo z plikami LESS/Sass - odpowiednie loadery potrafią “w locie” przetłumaczyć je na czysty CSS.

Za chwilę przekonasz się jak skonfigurować kilka najpopularniejszych loaderów, jakie powszechnie stosuje się w projektach front-endowych!

Loadery - konfiguracja

We wpisie z ubiegłego roku, w którym pokazałem podstawy konfiguracji webpacka w jego pierwszej wersji, nie zagłębiałem się za mocno w ten temat. Dziś postanowiłem pokazać trochę więcej przykładów. Dlatego też poniżej znajdziesz opis konfiguracji kilku najważniejszych loaderów, które zwykle używa się w każdym projekcie. Będą to loadery dla: CSS, Sass, Babel (ES6+) oraz plików graficznych.

Trzy sposoby użycia loaderów

Zanim przejdę do przedstawienia przykładów konfiguracji kilku najważniejszych loaderów, warto napisać najpierw kilka słów na temat instalacji loaderów oraz sposobów ich użycia. Jako, że loadery to zwykłe biblioteki dostępne w npm, ich instalacja przebiega w sposób właściwy dla tego repozytorium pakietów:

npm install --save-dev css-loader

lub jeśli używasz yarn (polecam - w dalszej części wpisu będę stosować właśnie yarn):

yarn add --dev css-loader

Kiedy loader mamy już zainstalowany, możemy go użyć. Mamy na to trzy sposoby

Po pierwsze, możemy użyć loadera w momencie importowania danego pliku w danym module:

require('css-loader!./styles.css');

W powyższy sposób mówimy webpackowi: kiedy ładujesz plik ./styles.css najpierw “potraktuj go” loaderem css-loader.

Drugim sposobem użycia loadera jest podanie odpowiednich parametrów podczas wywołania komendy webpack:

webpack --module-bind 'css=css-loader'

Ten sposób powoduje, że wszystkie pliki *.css ładowane przez moduły JavaScript będą “przepuszczane” przez css-loader.

Oba powyższe sposoby są dość niewygodne. W pierwszym trzeba zawsze pamiętać o podaniu odpowiednich loaderów. W drugim, jeśli loaderów będzie dużo (a zwykle tak jest), nasza komenda znacznie się wydłuży. Poza tym, przy powyższych sposobach, utrudniona jest dodatkowa konfiguracja używanych loaderów. Dlatego też najlepszym i najczęściej stosowanym sposobem jest trzecia opcja czyli konfiguracja loaderów w pliku webpack.config.js.

Sposobu tego użyję w kolejnych przykładach tak więc myślę, że będziesz mieć okazję dobrze się z nim zapoznać.

babel-loader

Pierwszym przykładem konfiguracji loaderów w webpack 2 będzie dodanie obsługi babela. Pozwoli nam to oczywiście pisać kod w ES6 - babel-loader będzie go tłumaczył “w locie” do ES5 co zapewni kompatybilność naszego projektu z dzisiejszymi przeglądarkami internetowymi.

Na początek instalacja:

yarn add --dev babel-loader babel-core babel-preset-env

Powyżej instalujemy trochę więcej pakietów niż tylko sam babel-loader - czasami niektóre loadery wymagają dodatkowych paczek, ale o tym musisz doczytywać w ich dokumentacji. Akurat w przypadku babel-loader wymagana jest instalacja pakietu babel-core. Jeśli natomiast chodzi o pakiet babel-preset-env to jest to po prostu “preset” Babela, czyli zestaw pluginów tego narzędzia, który pozwala na obsługę różnych elementów ES6 (i wyższych wersji). O tym przeczytasz w dokumentacji babela.

Następnie przechodzimy do utworzonego w poprzednim wpisie pliku webpack.config.js i dodajemy w nim nową właściwość module (przedstawiam cały plik, aby łatwiej było stwierdzić gdzie dokładnie dodać tę właściwość):

const path = require('path');
// const webpack = require('webpack');

module.exports = {
  entry: {
    home: './src/home.js',
    post: './src/post.js',
    contanct: './src/contact.js'
  },
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['env']
          }
        }
      }
    ]
  }
};

Jak widzisz powyżej, do właściwości module przypisujemy obiekt, który posiada właściwość rules. Jest to tablica obiektów, które definiują sposób obsługi różnych typów plików.

Spójrz na właściwości obiektu jaki znajduje się w tym momencie w tej tablicy. Pierwsza z nich: test, to wyrażenie regularne - pliki które spełniają to wyrażenie (czyli wszystkie pliki *.js) będą objęte działaniem naszego loadera. Kolejna właściwość to exclude. Tutaj również podajemy regex - pliki i katalogi, które go spełnią nie będą brane pod uwagę!

Na koniec najważniejsze czyli właściwość use. Przypisujemy do niej obiekt, który konfiguruje loader: właściwość loader określa jaki loader ma być użyty dla plików spełniających regex text. Możemy również podać dodatkowe opcje - służy do tego właściwość options (tutaj konfigurujemy babela aby użył presetu env). Obiekt przekazany do tej właściwości będzie się różnić w zależności od użytego loadera - warto zapoznać się z jego dokumentacją aby dowiedzieć się jakie opcje są dostępne.

Test konfiguracji

W ten sposób skonfigurowaliśmy babel-loader. Można teraz sprawdzić czy wszystko działa jak należy. Do tego celu, do naszego projektu dodałem plik ./src/libs/lib.js, który wygląda następująco:

export const x = 1;

Następnie w pliku ./src/home.js, który jest jednym z plików wejściowych webpacka, dodaję kod importujący wspomnianą “bibliotekę”:

import { x } from './libs/lib';

console.log(x);

Oba pliki zawierają więc kod ES6 (import oraz export). Teraz wystarczy uruchomić polecenie webpack aby przekonać się, że wszystko działa - webpack nie zgłosił błędu, a jeśli podpiąłbyś wynikowy “bundle” pod przeglądarkę to przekonałbyś się, że w konsoli faktycznie wyświetla się wartość “1”.

css-loader

Kolejnym bardzo często stosowanym loaderem jest css-loader. Pozwala on na ładowanie plików CSS w modułach JavaScript. Jak zwykle jednak, najpierw instalacja:

yarn add --dev style-loader css-loader

Zwróć uwagę, że oprócz css-loader instaluję również style-loader. Zalecane jest aby używać ich razem - css-loader potrafi po prostu zaimportować kod CSS do modułu JavaScript. Natomiast style-loader potrafi wstrzyknąć zaimportowany przez css-loader kod do pliku HTML za pomocą tagów link lub style. Na ten moment nie będziemy go wykorzystywać, ale w kolejnym wpisie pokażę jak zautomatyzować wstrzykiwanie generowanych “bundli” do pliku index.html.

Przejdźmy teraz ponownie do pliku webpack.config.js i spójrzmy na to co dodałem do tablicy rules:

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['env']
        }
      }
    },
    {
      test: /\.css$/,
      use: [ 'style-loader', 'css-loader' ]
    }
  ]
}

Tablica rules została uzupełniona o kolejny obiekt. Tym razem pliki spełniające regex przypisany do jego właściwości test mają rozszerzenie *.css.

Zwróć uwagę, że do właściwości use można też przypisać tablicę. W ten sposób nakazujemy webpackowi użyć jednocześnie dwóch loaderów! Jako, że nie ma potrzeby dodatkowej konfiguracji, tablica use zawiera dwie wartości typu string. Jeśli jednak chcielibyśmy dodać jakieś opcje konfiguracji, możemy zamiast “stringów” dodać do tej tablicy obiekty (takie same jak w poprzednim przykładzie - z właściwością loader oraz options).

Warto zajrzeć do dokumentacji loadera css-loader, ponieważ ma on całkiem spore możliwości konfiguracji!

Test konfiguracji

Czas na kolejny test. Do katalogu ./src dodałem plik styles.css. Jego treść jest następująca:

body {
  background: red;
}

Kolejna rzecz to import tego pliku stylów w pliku ./src/home.js:

import { x } from './libs/lib';
import './style.css';

console.log(x);

Teraz odpalamy polecenie webpack - znów żadnych błędów! Jeśli podpiąłbyś wynikowy “bundle” ./dist/bundle.min.js do pliku HTML zobaczyłbyś, że tło dokumentu jest czerwone.

sass-loader

Dość częstą praktyką jest stosowanie w projektach pre-procesorów CSS takich jak Sass. Do tego celu istnieją też odpowiednie loadery webpacka! Jednym z nich jest sass-loader, którego przykładową konfigurację przedstawię za chwilę. Najpierw jednak, oczywiście instalacja:

yarn add --dev sass-loader node-sass

Zwróć uwagę na konieczność instalacji pakietu node-sass.

Konfiguracja tego loadera jest bliźniaczo podobna do konfiguracji css-loader. Zwykle obie konfiguracje współistnieją w jednym pliku webpack.config.js, ponieważ najczęściej oprócz naszych własnych plików *.scss wykorzystujemy też w projektach jakieś zewnętrzne biblioteki, które mogą posiadać style *.css. Warto więc aby webpack umiał je w razie czego zaczytać. Poniżej przedstawiam przykład konfiguracji sass-loader:

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['env']
        }
      }
    },
    {
      test: /\.css$/,
      use: [ 'style-loader', 'css-loader' ]
    },
    {
      test: /\.scss$/,
      use: [ 'style-loader', 'css-loader', 'sass-loader' ]
    }
  ]
}

Zwróć uwagę, że wraz z sass-loader używamy loaderów css-loader oraz style-loader. To dlatego, że finalnie chcemy uzyskać ten sam efekt - sass-loader tłumaczy kod Sass na CSS, css-loader umie zaimportować go w module JavaScript, a style-loader będzie pomocny przy załączaniu tego kodu w pliku HTML. Z tego wniosek, że aby uzyskać pewne efekty można wykorzystywać kilka loaderów na raz, przekazując wynik jednego loadera na wejście innego.

Test konfiguracji

Jak poprzednio, czas teraz przetestować powyższą konfigurację. Do katalogu ./src dodałem plik style.scss, którego zawartość jest następująca (kod Sass):

$primary-color: blue;

body {
  color: $primary-color;
}

Następnie rozszerzam kod pliku ./src/home.js:

import { x } from './libs/lib';
import './style.css';
import './style.scss';

console.log(x);

Po odpaleniu polecanie webpack znów wszystko pięknie działa.

Ładowanie obrazków

Do ładowania obrazków w webpack służą dwa loadery: file-loader lub url-loader. Oba mają trochę inne opcje konfiguracyjne. Ja, dla naszego przykładu, posłużę się tym drugim. Jak zwykle najpierw instalacja:

yarn add --dev url-loader

Skoro loader został już zainstalowany, czas go skonfigurować. Spójrz na sekcje module po dodaniu konfiguracji tego loadera:

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['env']
        }
      }
    },
    {
      test: /\.css$/,
      use: [ 'style-loader', 'css-loader' ]
    },
    {
      test: /\.scss$/,
      use: [ 'style-loader', 'css-loader', 'sass-loader' ]
    },
    {
      test: /\.(png|jpg|gif)$/,
      use: [
        {
          loader: 'url-loader',
          options: {
            limit: 8192
          }
        }
      ]
    }
  ]
}

Jak widzisz, loadera url-loader używamy dla plików o rozszerzeniach: *.png, *.jpg oraz *.gif. Loader ten pozwala też ustawić limit maksymalnej wielkości pliku. Zaletą loaderów obrazków jest to, że potrafią one obsłużyć także ładowanie plików graficznych w plikach CSS (oczywiście loader dla CSS też musi być wtedy skonfigurowany).

Test konfiguracji

Czas na ostatni test! W katalogu ./src utworzyłem katalog images i wrzuciłem tam plik webpack.png. Następnie dodałem import tego pliku w module JavaScript:

import { x } from './libs/lib';
import './style.css';
import './style.scss';
import image from './images/webpack.png';

console.log(x);

Po odpaleniu polecenia webpack oczywiście wszystko działa. Można by zapytać po co ładować pliki graficzne w ten sposób? Przyda je się to na przykład w kodzie komponentów React - zamiast podawać w atrybucie src elementu img ścieżkę do zasobu można podstawić tam obiekt zaimportowany w powyższy sposób. Jest to o tyle wygodniejsze, że nie trzeba martwić się o to, jak ścieżka będzie wyglądała na produkcji. Webpack umieści wszystko w “bundlu” (o tym jak powydzielać pewne elementy z “bundla” w kolejnej części).

Podsumowanie

Loadery w webpacku to bardzo przydatna rzecz. Odpowiadają one za większość zadań jakie webpack ma wykonać podczas budowania plików wynikowych. Dziś znów wyszedł mi całkiem długi wpis, więc na pewno będzie jeszcze co najmniej jedna część o webpacku, bo zostało mi jeszcze parę ważnych rzeczy do opisania!

P.S. Na koniec tej mini-serii o webpacku podam linka do repozytorium GitHuba, gdzie będziesz mógł/mogła znaleźć kod przedstawianych przeze mnie przykładów!


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+: