Zpracování CSS stylů

V minulé části jsme začali s konfigurací Webpacku a zabalili jsme si náš JavaScript kód. V této části si naši konfiguraci rozšíříme o zpracování souborů s CSS styly.

Pokud z nějakého důvodu nemáte kód z minulé části, tak si můžete stáhnout startovní kód pro tuto část. Po stažení nezapomeňte nainstalovat balíčky pomocí příkazu "npm install".

Importování CSS a LESS souboru

O našich CSS a LESS souborech musíme dát Webpacku nějakým způsobem vědět. Webpack začíná náš kód zpracovávat souborem index.js ve složce assets/js. Kromě JS modulů můžeme pro Webpack v JavaScriptu importovat i jiné soubory jako jsou CSS styly, obrázky a podobně. Když na ně Webpack narazí, tak je také zpracuje. V základu ale Webpack rozumí jen JavaScriptu a JSON souborům, takže bez žádné další konfigurace se mu jiné soubory zpracovat nepovede. Budeme k tomu potřebovat nainstalovat loadery, ale tím se budeme zabývat později. Teď si zatím naimportujeme naše styly do JavaScriptu. A než to patlat do našeho JavaScript kódu, tak si na to v kořenové složce vytvoříme nový JavaScript soubor, který bude sloužit k naimportování různých věcí. Můžeme jej nazvat také třeba index.js. Do tohoto souboru naimportujeme náš JavaScript kód a stejným způsobem i CSS a LESS soubor. CSS soubor, který budeme importovat se nachází ve složce assets/css a jmenuje se header.css. LESS soubor se nachází ve složce less a jmenuje se main.less. Soubor main.less slouží k naimportování ostatních LESS souborů do jednoho.

import './assets/js/index.js';
import './assets/css/header.css';
import './less/main.less';

Protože teď budeme chtít, aby Webpack začal náš kód zpracovávat naším nově vytvořeným souborem, tak to budeme muset nastavit v konfiguračním souboru.

const path = require("path");

module.exports = {
    entry: "./index.js",
    output: {
        filename: "index.js",
        path: path.resolve(__dirname, "dist"),
        clean: true
    }
}

Protože budeme ze začátku načítat CSS styly prostřednictvím JavaScriptu a až poté pro ně v dist složce vytvoříme vlastní soubor, tak ještě v HTML souboru odstraňte odkazy na CSS styly a ujistěte se, že když aplikaci spustíte, uvidíte nenastylovanou stránku.

Konfigurace Loaderů

Když si Webpack zkusíte spustit prostřednictvím build skriptu, který jsme si vytvořili v minulé části (npm run build), tak vám to vyhodí chybu. Webpack totiž neví jak zpracovat soubory, které ve vstupním JavaScript souboru importujeme. V základu rozumí jen JavaScript (a JSON souborům). Pokud chceme zpracovávat i jiné typy souborů, tak si na to musíme nainstalovat a nakonfigurovat loadery.

Loadery které budeme potřebovat pro zpracování CSS a LESS souborů, jsou css-loader, style-loader a less-loader. Nainstalovat je můžeme jako jakékoliv jiné NPM balíčky.

npm install --save-dev css-loader style-loader less-loader

Po jejich instalaci je můžeme přidat do našeho konfiguračního souboru. Loadery přidáváme do pole, které nastavujeme jako hodnotu vlastnosti rules v objektu, který nastavujeme jako hodnotu pro vlastnost module. Lépe je to vidět v ukázce níže. Každá položka v poli rules je objekt, který má nakonfigurováno, které loadery se spustí pro jaký typ souboru.

const path = require("path");

module.exports = {
    entry: "./index.js",
    output: {
        filename: "index.js",
        path: path.resolve(__dirname, "dist"),
        clean: true
    },
    module: {
        rules: [
            {
                test: /\.css$/i, // všechny soubory s koncovkou .css
                use: ["style-loader", "css-loader"]
            },
            {
                test: /\.less$/i, // všechny soubory s koncovkou .less
                use: ["style-loader", "css-loader", "less-loader"]
            }
        ]
    }
}

Je nutno podotknout, že loadery, které specifikujeme ve vlastnosti use se spouští od konce na začátek pole. Pro LESS soubory se tedy nejdříve spustí less-loader, poté css-loader a nakonec style-loader. Zde je popis co jednotlivé loadery dělají:

  • less-loader - kompiluje LESS na CSS
  • css-loader - bere CSS a kompiluje jej do JavaScriptu
  • style-loader - bere CSS v JavaScriptu a aplikuje jej na stránku

Asi si říkáte proč kompilujeme CSS do JavaScriptu. Pro produkci je to samozřejmě nesmysl, ale pro vývoj je to lepší, protože je to rychlejší. Později budeme mít jeden konfigurační soubor pro vývoj a druhý pro produkci. V konfiguraci pro vývoj budeme takto kompilovat CSS do JavaScriptu a v konfigurace pro produkci budeme vytvářet CSS soubor. Chtěl jsem vám ale již teď ukázat, jak kompilaci CSS stylů do JavaScriptu provést. Zachvíli to změníme a budeme pro styly vytvářet samostatný CSS soubor. Když si teď spustíte Webpack pomocí příkazu "npm run build", tak budou styly obsaženy v JavaScript souboru. Když aplikaci v prohlížeči otevřete, stránka by měla být nastylovaná.

Vytvoření samostatného CSS souboru

Kompilovat CSS do JavaScriptu je pro produkci nesmysl. Proto teď naši konfiguraci loaderů trochu upravíme. Namísto style-loaderu, který aplikuje CSS v JavaScriptu na stránku, použijeme MiniCssExtractPlugin. Nejedná se o loader, ale o plugin, který obsahuje loader. Pluginy lze využít k provádění širší škály úkolů jako je optimalizace, asset management, a tak podobně. MiniCssExtractPlugin vytáhne styly z JavaScriptu, které tam zkompiloval css-loader a vytvoří pro ně samostatný CSS soubor. Můžeme jej nainstalovat následujícím příkazem.

npm install --save-dev mini-css-extract-plugin

Do našeho konfiguračního souboru si MiniCssExtractPlugin naimportujeme a použijeme jej. Pluginy přidáváme do pole, které předáváme vlastnosti plugins. Jeden plugin můžeme použít i vícekrát, proto je potřeba vytvořit instanci pluginu. MiniCssExtractPlugin obsahuje také loader, takže ten je potřeba také použít. Jak se plugin používá si vždy můžete najít někde v dokumentaci. Následující ukázka ukazuje předělaný konfigurační soubor.

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
    entry: "./index.js",
    output: {
        filename: "index.js",
        path: path.resolve(__dirname, "dist"),
        clean: true
    },
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: [MiniCssExtractPlugin.loader, "css-loader"]
            },
            {
                test: /\.less$/i,
                use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin()
    ]
}

Když teď Webpack spustíme, tak se nám v dist složce vygeneruje samostatný CSS soubor nazvaný jako main.css. V JavaScriptu by již styly být obsaženy neměli. Když si aplikaci v prohlížeči zkusíte spustit, tak stránka nastylovaná nebude. Takže pokud chcete mít v aplikaci znova obsažené styly, musíte přidat odkaz na styly ve složce dist.

<link rel="stylesheet" href="./dist/main.css">

Prefixnutí novějších CSS vlastností

Pokud v našem CSS kódu používáme vlastnosti, které třeba některé prohlížeče zatím ještě úplně nepodporují, tak je můžeme prefixnout. Díky tomu se tedy použijí i v prohlížečích, které je zatím podporují jen experimentálně. Pokud nevíte co to prefixy jsou, tak tady je ukázka:

.trida {
    -webkit-transition: all 1s linear;
    -moz-transition: all 1s linear;
    -ms-transition: all 1s linear;
    -o-transition: all 1s linear;
    transition: all 1s linear;
}

Jak vidíte, prefixů je více a psát je ručně by byla otrava. Každý prohlížeč má jiný prefix.

  • -webkit-: Chrome, Safari, iOS WebView, Android
  • -moz-: Firefox
  • -ms-: Edge, Internet Explorer
  • -o-: Opera

Pro automatické prefixnutí novějších CSS vlastností můžeme použít postcss-loader. Jedná se o loader, který nám umožňuje zpracovat CSS s použitím PostCSS. Je takový nástroj, který nám umožňuje zpracovávat CSS prostřednictvím JavaScript pluginů a můžeme jej klidně použít i samostatně bez Webpacku.

Abychom mohli postcss-loader použít, tak musíme nainstalovat také samotné PostCSS. K automatickému prefixnutí použijeme PostCSS plugin jménem "postcss-preset-env", takže ten si musíme také nainstalovat. Nainstalujeme si tedy tyto 3 balíčky: "postcss-loader", "postcss" a "postcss-preset-env".

npm install --save-dev postcss-loader postcss postcss-preset-env

Až se nám balíčky nainstalují, tak si postcss-loader můžeme přidat do naší konfigurace. Kam jej přidat a jak jej nastavit ukazuje následující ukázka. To si většinou najdete někde v dokumentaci. Většina konfigurace Webpacku je hlavně o googlování. Já jsem toto nastavení například zkopíroval z dokumentace. Určitě to není tak, že to všechno mám v hlavě.

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
    entry: "./index.js",
    output: {
        filename: "index.js",
        path: path.resolve(__dirname, "dist"),
        clean: true
    },
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: [
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    {
                        loader: "postcss-loader",
                        options: {
                            postcssOptions: {
                                plugins: [
                                    ["postcss-preset-env", {}]
                                ]
                            }
                        }
                    }
                ]
            },
            {
                test: /\.less$/i,
                use: [
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    {
                        loader: "postcss-loader",
                        options: {
                            postcssOptions: {
                                plugins: [
                                    ["postcss-preset-env", {}]
                                ]
                            }
                        }
                    },
                    "less-loader"
                ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin()
    ]
}

Jak vidíte v předchozí ukázce, tak pokud chceme loader nějakým způsobem nastavit, předáme namísto obyčejného řetězce objekt. V tomto objektu můžeme pomocí vlastnosti options loader nějakým způsobem nastavit.

Nyní můžeme Webpack spustit a ve vygenerovaném CSS kódu by mohli být nějaké CSS vlastnosti prefixnuté. V našem kódu ale nebude nic prefixnutého, protože ve stylech nepoužíváme nic nového nebo nestandardního. Když si ale styly zkusíte někde upravit a nastavíte třeba vlastnost user-select na none, tak by se tato vlastnost mohla prefixnout. Mě se prefixnula když jsem to zkoušel. Pokud ale tento tutoriál čtete někdy v daleké budoucnosti, mohlo se to trochu změnit a už se to prefixnout nemusí. Každopádně si třeba můžete zkusit ve složce assets/css otevřít soubor header.css a tuto vlastnost si do něj přidat.

.header {
    padding: .8rem;
    text-align: center;
    background-color: #357F8C;

    user-select: none;
}

Zminifikování CSS souboru

Poslední věc, kterou bychom mohli s našimi styly udělat, je minifikovat výsledný CSS soubor. K tomu využijeme plugin jménem CssMinimizerWebpackPlugin. Nainstalujeme jej pomocí následujícího příkazu:

npm install --save-dev css-minimizer-webpack-plugin

Poté co se nám CssMinimizerWebpackPlugin nainstaluje, tak jej můžeme použít. Používá se ale trochu jinak než jiné pluginy. To si můžete zjistit v dokumentaci. Nastavíme jej v části optimization jako minimizer.

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = {
    entry: "./index.js",
    output: {
        filename: "index.js",
        path: path.resolve(__dirname, "dist"),
        clean: true
    },
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: [
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    {
                        loader: "postcss-loader",
                        options: {
                            postcssOptions: {
                                plugins: [
                                    ["postcss-preset-env", {}]
                                ]
                            }
                        }
                    }
                ]
            },
            {
                test: /\.less$/i,
                use: [
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    {
                        loader: "postcss-loader",
                        options: {
                            postcssOptions: {
                                plugins: [
                                    ["postcss-preset-env", {}]
                                ]
                            }
                        }
                    },
                    "less-loader"
                ]
            }
        ]
    },
    optimization: {
        minimizer: [
            `...`,
            new CssMinimizerPlugin()
        ]
    },
    plugins: [
        new MiniCssExtractPlugin()
    ]
}

V předchozí ukázce můžete vidět, že do pole minimizer předáváme ještě řetězec `...`. To zajišťuje, aby se nepřepsal defaultní minimizer jménem TerserPlugin, který je ve Webpacku defaultně nastavený. Slouží pro minifikování JavaScriptu. Pokud bychom jej tedy přepsali, náš JavaScript kód by se nezminifikoval.

Teď můžeme Webpack spustit a náš výsledný CSS soubor by měl být zminifikovaný.

S konfigurací pro zpracování našich CSS stylů jsme hotovi. V příští části zařídíme, aby se nám do dist složky kopíroval také HTML soubor a obrázek.