Webpack - krok za krokem - loadery a pluginy

01.02.2019 Programování #webpack

Pokračování článku Webpack - krok za krokem


6. Loadery a pluginy

Nyní se budeme zabývat tím, co webpack může udělat automaticky prostřednictvím některých konfigurací.

6.1. Loadery

Webpack umožňuje použití loaderů k průběžnému zpracování souborů. To umožní sdružovat libovolný statický zdroj mimo JavaScript. Vzhledem k tomu, že webpack stále neví, co dělat s těmito loadery, webpack config objekt používá module vlastnost, aby zjistil, jaký loader má fungovat a jak je provádět.

module vlastnost samotného config objektu jeho objektem. Pracuje s některými dalšími možnostmi uvedenými níže:

# module.noParse

Zabraňuje zpracování webových souborů z libovolného souboru odpovídající danému datu RegExp. Ignorované soubory by neměly být volány pro import, požadavek, definici nebo jakýkoli jiný mechanismus importu. To může zvýšit výkon při ignorování velkých knihoven.

module: {
  noParse: /jquery|lodash/
}
# module.rules

Každý loader se nastavuje jako soubor pravidel uvnitř array. Zatímco každý prvek tohoto pole je předmětem obsahujícím jednotlivé loadery a jejich příslušné konfigurace.

Pravidlo lze rozdělit na tři části - Podmínky, Výsledky a Vnořená pravidla. 
1. Podmínky: Existují dvě vstupní hodnoty pro podmínky 
 a. Zdroj: Absolutní cesta k požadovanému souboru. 
 b. Vydavatel: Umístění importu.

V pravidle vlastností testincludeexcluderesource jsou porovnány se zdrojem a vlastnosti issuer je uzavřeno s vydavatelem.

2. Výsledky: Používají pouze tehdy, když se shoduje podmínka Pravidla. Existují dvě výstupní hodnoty pravidla:
 a. Použité loadery: Množina loaderů použitých na prostředek. 
 b. Možnosti parseru: Objekt voleb, který by měl být použit k vytvoření parseru pro tento modul.

3. Vnořená pravidla: V rámci vlastností rules oneOf. Tato pravidla jsou vyhodnocována, když se splňuje podmínka Pravidla.

Teď jednoduššejí :)

Loader potřebuje některé další informace, aby v modulu fungoval správně a efektivně. Uvádíme je module.rules s některými konfiguračními parametry.

test: (povinné) Loader musí vědět, s jakou příponou bude pracovat. definujeme pomocí RegExp.

test: /\.js$/

include: (volitelně) Loader potřebuje adresář, aby zjistil, kde jsou uloženy pracovní soubory.

include: /src/

exclude:(volitelně) Můžeme ušetřit spoustu nežádoucích procesů - nechceme analyzovat moduly uvnitř adresáře node_modules a ušetříme spoustu paměti a času provádění.

exclude: /node_modules/

use:(požadováno) Pravidlo musí mít vlastnost loader, která je řetězecem. Uvedete loadery, které chcete použít pro daný úkol. Loadery mohou být řetězeny složením více loaderů, které budou aplikovány zprava doleva (poslední nakonfigurované). 
Může mít vlastnost options, která je řetězcem nebo objektem. Tato hodnota je předána loaderu, který by ji měl interpretovat načtením. 
Pro kompatibilitu je také možné použít vlastnost query, což je alias vlastnosti property. Použijte options místo property.

use: {
  loader: "babel-loader",
  options: {
    presets: ['env']
  }
}

Detail konfigurace module.ruleshttps://webpack.js.org/configuration/module/#module-rules

6.2. Plguiny

Možnost plugins se používá k přizpůsobení procesu vytváření webového balíčku. Webpack je dodáván s řadou vestavěných pluginů dostupných pod webpack.[plugin-name]

Webpack má v konfiguračním objektu vlastnost plugins pro konfiguraci pluginu.

7. Některé LOADERY a PLUGINY v akci

7.1. clean-webpack-plugin (PLUGIN)

Pokaždé, když chceme vidět naši produkční dist složku, je třeba ji vymazat. clean-webpack-plugin provádí odstranění / čištění složek sestavení před buildováním. Nastavení je velmi snadné:

Instalovat přes npm

> npm i -D clean-webpack-plugin

Import do souboru webpack.config.js

const CleanWebpackPlugin = vyžaduje ('clean-webpack-plugin');

Nyní budeme poprvé používat plugin. Webpack má v objektu config nastavení konfigurace pluginu. Každý plugin potřebuje vytvořit instanci, která bude použita v objektu config. Takže definování ve vlastnostech je následující:

plugins: [
  new CleanWebpackPlugin(['dist'])
]

Zde jsme v příkladu clean-webpack-plugin zmínili dist jako prvek pole. Webpack nyní ví, že chceme vyčistit / odstranit složku dist pokaždé před buildováním našeho balíčku. Tato instance může mít více adresářů / cest jako prvků pole a více možností jako objekt.

Syntaxe pro použití clean-webpack-webpack:

plugins: [
  new CleanWebpackPlugin(paths [, {options}])
]

Měli byste sledovat proces odstranění a vytváření vašeho adresáře živě a také ve vašem IDE.

Nyní bude webpack.config.js vypadat následovně:

const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');

const config = {
  context: path.resolve(__dirname, 'src'),
  entry: {
    // removing 'src' directory from entry point, since 'context' is taking care of that
    app: './app.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: './assets/js/[name].bundle.js'
  },
  plugins: [
    new CleanWebpackPlugin(['dist'])
  ],
  devServer: {
    contentBase: path.resolve(__dirname, "./dist/assets/media"),
    compress: true,
    port: 12000,
    stats: 'errors-only',
    open: true
  },
  devtool: 'inline-source-map'
}

module.exports = config;

7.2. babel-loader (LOADER)

Všichni chceme napsat nějaký kód ES2015 / ES6, ne? Ale až dosud náš prohlížeč není plně schopný interpretovat syntaxi ES6, musíme nejprve přenést náš ES6 kód do ES5 a pak ho použít v našem balíčku. Babel přebírá tuto odpovědnost za nás. Prostě potřebujeme do naší konfigurace zahrnout babel-loader, který bude dělat za nás několik jednoduchých kroků.

Nainstalujte babel-loader

> npm i -D babel-core babel-loader

Vytvořte soubor .babelrc v kořenovém adresáři projektu, abyste povolili některé přednastavení babel (již jsme to udělali v části nastavení projektu. Již jej najdete v kořenovém adresáři projektu)

Nainstalujte jej babel-preset-env pro kompilaci závislou na prostředí

> npm i -D babel-preset-env

Chcete-li povolit přednastavení, musíte jej definovat ve vašem souboru .babelrc, například takto:

{
  "presets": ["env"]
}

Toto lze také dát také do souboru package.json, potom není nutné .babelrc.

"scripts": {
  "babel": {
    "presets": ["env"]
  }
}

Tato vlastnost lze vložit do příslušného modulu v konfiguračním souboru

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

Z našeho zavaděče víme, že:: 
test nechá loader zjistit, se kterými formáty bude pracovat 
include :nechá loader zjistit, do kterého adresáře bude ukládat 
exclude :nechá loader zjistit, kterému adresář by se měl vyhnout při parsování 
use : nechá loader zjistit, s jakým konkrétním loaderem používá use.loader a s jakými konfiguračními možnostmi use.options

Webpack konfigurační soubor:

const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');

const config = {
  context: path.resolve(__dirname, 'src'),

  entry: {
    // removing 'src' directory from entry point, since 'context' is taking care of that
    app: './app.js'
  },

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: './assets/js/[name].bundle.js'
  },

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

  plugins: [
    new CleanWebpackPlugin(['dist'])
  ],

  devServer: {
    contentBase: path.resolve(__dirname, "./dist/assets/media"),
    compress: true,
    port: 12000,
    stats: 'errors-only',
    open: true
  },

  devtool: 'inline-source-map'
}

module.exports = config;

Konfigurace v .babelrc

{
  "presets": ["env"]
}

7.3. html-loader (LOADER) a html-webpack-plugin (PLUGIN)

Protože chceme upravit dotazy index.html src adresáři a chceme vidět změny ve výstupním dist adresáři, musíme vytvořit a aktualizovat index.html do dest pokaždé, když webpack provede build náš projekt.

Pro vyřešení našeho problému potřebujeme společně použít loader a plugin. Protože - 
html-loader : Exportuje HTML jako řetězec. HTML je minimalizováno, když to překladač požaduje. 
html-webpack-plugin : Zjednodušuje vytváření souborů HTML, které budou sloužit vašim svazkům webpacku.

Instalace závislostí

> npm i -D html-loader html-webpack-plugin

Konfigurace html-loader

{ test: /\.html$/, use: ['html-loader']

Import html-webpack-plugin

const HtmlWebpackPlugin = require('html-webpack-plugin');

Použití pluginu

plugins: [
  new HtmlWebpackPlugin({
    template: 'index.html'
  })
]
const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const config = {
  context: path.resolve(__dirname, 'src'),

  entry: {
    // removing 'src' directory from entry point, since 'context' is taking care of that
    app: './app.js'
  },

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: './assets/js/[name].bundle.js'
  },

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

  plugins: [
    new CleanWebpackPlugin(['dist']),
    //html-webpack-plugin instantiation
    new HtmlWebpackPlugin({
      template: 'index.html'
    })
  ],

  devServer: {
    contentBase: path.resolve(__dirname, "./dist/assets/media"),
    compress: true,
    port: 12000,
    stats: 'errors-only',
    open: true
  },

  devtool: 'inline-source-map'
}

module.exports = config;

Teď je nastavena každá věc. Ve svém src/index.html můžeme něco psát a pak spustit příkazy pro sestavení 
npm run dev : Uvidíte, že naše aplikace nyní funguje a můžete vidět html prvek v prohlížeči. Vložený soubor js byl vložen do html před koncem značky body 
npm run build:prod : Sledujte proces vytváření výstupu index.html a změny použité v dist / index.html

Zdroje: 
html-loader: webpack-docgithub-doc
html-webpack-plugin: webpack-docgithub-doc

7.4. css-loader (LOADER), style-loader (LOADER), sassloader (LOADER), extract-text-webpack-plugin (PLUGIN)

Použití CSS a SASS s webovým balíčkem může vypadat jako nějaký další problém s některými dalšími kroky. Webpack zkompiluje css a tlačí kód do balíčku js. Ale potřebujeme jej extrahovat ze svazku a poté vytvořit stejný .css soubor a pak ho zasunout do našeho dist/index.html a pak přidat css do DOM. Ne doslovně.

Zkombinujme 3 loadery a jeden plugin, aby jsme je viděli společně pracovat na požadovaném výstupu: 
style-loader Přidá CSS do DOM vložením < style > značky 
css-loader : Interpretuje @import a url() pomocí import/require()
sass-loader : Načte soubor SASS/SCSS a zkompiluje ho do CSS  
node-sass : Poskytuje vazbu pro Node.js do LibSass. Sass-loader vyžaduje node-sass a webpack jako peerDependency. Tímto způsobem můžete přesně řídit verze.
extract-text-webpack-plugin : Extrahujte text ze svazku nebo svazků do samostatného souboru.

Instalace závislostí

> npm i -D sass-loader node-sass css-loader style-loader extract-text-webpack-plugin

Musíme importovat naše app.scss do naší app.js, abychom nechali webpack vědět o závislostech. Takže v našem app.js budeme psát:

import './assets/scss/app.scss';

Kromě toho zkontrolujte, zda naše moduly sass fungují dobře, přidejte ještě jeden soubor .scss do našeho src/assets/scss adresáře přes terminal

> touch src/assets/scss/_colors.scss

Importujte nově vytvořený _color.scss soubor app.scss a přidejte styl s:

@import '_colors';
body {
  background: $bgcolor;
}

a definujte $bgcolor v souboru _color.scss:

$bgcolor : #e2e2e2;

Importujte extract-text-webpack-plugin do konfiguračního souboru

const ExtractTextPlugin = require('extract-text-webpack-plugin');

Musíme importovat webpack do našeho webpack.config.js pro použití pluginu:

const webpack = require('webpack');

Nyní se náš pracovní postup rozděluje na dva typy požadavků. 
(1) Chceme mít pouze jeden .css soubor do našeho výstupu 
(2) Chceme více než jeden .css soubory jako výstup

Pro jeden stylový soubor (pracujeme takto):
Nejprve musíme vytvořit instanci, ExtractTextPlugindo které budeme definovat náš výstupní název souboru:

const extractPlugin = new ExtractTextPlugin({
  filename: './assets/css/app.css'
});

Zadruhé, při konfiguraci našich loaderů css/sass musíme použít dříve vytvořenou instanci s její extract() metodou (tj. extractPlugin.extract()) A uvnitř metody předáme požadované loadery jako argument ve formě objektu.

Takže naše konfigurace těchto loaderů bude:

{
  test: /\.scss$/,
  include: [path.resolve(__dirname, 'src', 'assets', 'scss')],
  use: extractPlugin.extract({
    use: ['css-loader', 'sass-loader'],
    fallback: 'style-loader'
  })
}

Nyní přídáme instanci z ExtractTextPlugin (která je extractPlugin) uvnitř sekce plugins:

plugins: [
  extractPlugin
]

Jestliže nebudeme používat plugin html-loader, můžeme použít klasický způsob načtení js a css souborů:

<link rel="stylesheet" href="./assets/app.css">
<script src="./assets/main.bundle.js"></script>

Pro více stylů (pouze pro příklady):

Práce s více styly není noc nového, protože se vychází z předchozích znalostí pro jediný styl.

Stačí vytvořit tolik instancí, ExtractTextPlugin kolik chcete, podle počtu stylových souborů. Vytváříme zde dvě instance, jednu pro css a jednu pro kompilovanou sass

const extractCSS = new ExtractTextPlugin('./assets/css/[name]-one.css');
const extractSASS = new ExtractTextPlugin('./assets/css/[name]-two.css');

Loadery budou vypadat:

{ test: /\.css$/, use: extractCSS.extract([ 'css-loader', 'style-loader' ]) },
{ test: /\.scss$/, use: extractSASS.extract([ 'css-loader', 'sass-loader' ], fallback: 'style-loader') }

Přidání instancí do pluginu:

plugins: [
  extractCSS,
  extractSASS
]

Nyní jej spusťte npm run dev pro zobrazení ve vašem prohlížeči.

Pro zobrazení případných debugovacích chyb, zavedeme do zdrojové mapy sourceMap:true.

{
  test: /\.scss$/,
  include: [path.resolve(__dirname, 'src', 'assets', 'scss')],
  use: extractPlugin.extract({
    use: [
      {
        loader: 'css-loader',
        options: {
          sourceMap: true
        }
      },
      {
        loader: 'sass-loader',
        options: {
          sourceMap: true
        }
      }
    ],
    fallback: 'style-loader'
  })
}

Ověřte zda se provedly změny ve stylech a zobraste si výsledek v prohlížeči. 

const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const webpack = require('webpack');

const extractPlugin = new ExtractTextPlugin({ filename: './assets/css/app.css' });

const config = {

  context: path.resolve(__dirname, 'src'),

  entry: {
    app: './app.js'
  },

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: './assets/js/[name].bundle.js'
  },

  module: {
    rules: [

      //babel-loader
      {
        test: /\.js$/,
        include: /src/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['env']
          }
        }
      },
      //html-loader
      { test: /\.html$/, use: ['html-loader'] },
      //sass-loader
      {
        test: /\.scss$/,
        include: [path.resolve(__dirname, 'src', 'assets', 'scss')],
        use: extractPlugin.extract({
          use: [
            {
              loader: 'css-loader',
              options: {
                sourceMap: true
              }
            },
            {
              loader: 'sass-loader',
              options: {
                sourceMap: true
              }
            }
          ],
          fallback: 'style-loader'
        })
      }

    ]
  },

  plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      template: 'index.html'
    }),
    extractPlugin
  ],

  devServer: {
    contentBase: path.resolve(__dirname, "./dist/assets/media"),
    compress: true,
    port: 12000,
    stats: 'errors-only',
    open: true
  },

  devtool: 'inline-source-map'
  
}

module.exports = config;

7.5. Souborový loader (LOADER)

Máne nastavenou konfiguraci pro každou věc kromě statických souborů, jako jsou obrázky, písma. Nyní si nastavíme statické soubory s velmi užitečným načítáním file-loader

Nainstalujte zavaděč souborů

> npm i -D file-loader

Konfigurace file-loader

{
  test: /\.(jpg|png|gif|svg)$/,
  use: [
    {
      loader: 'file-loader',
      options: {
        name: '[name].[ext]',
        outputPath: './assets/media/',
        publicPath: './assets/media/'
      }
    }
  ]
}

Nemusíme přidávat publicPath a přidáme načítání souborů s fonty:

//file-loader(for images)
{
  test: /\.(jpg|png|gif|svg)$/,
  use: [
    {
      loader: 'file-loader',
      options: {
        name: '[name].[ext]',
        outputPath: './assets/media/'
      }
    }
  ]
},
//file-loader(for fonts)
{
  test: /\.(woff|woff2|eot|ttf|otf)$/,
  use: ['file-loader']
}

Pomocí loaderu konfigurujeme umístění fontů, které můžete importovat pomocí deklarace @font-face. Do souboru kaskádového stylu vložte:

@font-face {
  font-family: 'MyFont';
  src:  url('./my-font.woff2') format('woff2'),
        url('./my-font.woff') format('woff');
  font-weight: 600;
  font-style: normal;
}

Nyní přidejte obrázky do adresáře src/assets/media a vytvořte element img v souboru src/index.html.

const path = require('path');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const webpack = require('webpack');

const extractPlugin = new ExtractTextPlugin({ filename: './assets/css/app.css' });

const config = {

  context: path.resolve(__dirname, 'src'),

  entry: {
    app: './app.js'
  },

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: './assets/js/[name].bundle.js'
  },

  module: {
    rules: [

      //babel-loader
      {
        test: /\.js$/,
        include: /src/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['env']
          }
        }
      },
      //html-loader
      { test: /\.html$/, use: ['html-loader'] },
      //sass-loader
      {
        test: /\.scss$/,
        include: [path.resolve(__dirname, 'src', 'assets', 'scss')],
        use: extractPlugin.extract({
          use: [
            {
              loader: 'css-loader',
              options: {
                sourceMap: true
              }
            },
            {
              loader: 'sass-loader',
              options: {
                sourceMap: true
              }
            }
          ],
          fallback: 'style-loader'
        })
      },
      //file-loader(for images)
      {
        test: /\.(jpg|png|gif|svg)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: './assets/media/'
            }
          }
        ]
      },
      //file-loader(for fonts)
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ['file-loader']
      }

    ]
  },

  plugins: [
    new CleanWebpackPlugin(['dist']),
    new HtmlWebpackPlugin({
      template: 'index.html'
    }),
    extractPlugin
  ],

  devServer: {
    contentBase: path.resolve(__dirname, "./dist/assets/media"),
    compress: true,
    port: 12000,
    stats: 'errors-only',
    open: true
  },

  devtool: 'inline-source-map'

}

module.exports = config;

Spusťte  npm run dev a npm run build:prod a ověřte si jak vše funguje.