Загрузчики (loaders)

Загрузчики (loaders) — механизм преобразования файлов в процессе сборки. По умолчанию Webpack понимает только JavaScript и JSON, поэтому любые другие типы ресурсов требуют дополнительной обработки.

Загрузчики позволяют:

  • преобразовывать современный JavaScript в совместимый код;
  • компилировать TypeScript;
  • обрабатывать CSS, SCSS и Less;
  • подключать изображения и шрифты;
  • импортировать HTML-фрагменты;
  • преобразовывать JSX;
  • оптимизировать ресурсы перед попаданием в bundle.

Webpack рассматривает любой импорт как модуль. Это означает, что можно импортировать не только Jav * aScript:

import './styles/main.scss';
import logo fr om './images/logo.png';
import data from './data/users.json';

Однако без соответствующих загрузчиков большинство таких импортов вызовет ошибку.


Принцип работы loaders

Каждый loader представляет собой функцию преобразования.

Webpack передаёт содержимое файла загрузчику, а загрузчик возвращает преобразованный результат.

Обработка происходит по цепочке.

Пример:

module.exports = {
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader'
                ]
            }
        ]
    }
};

Последовательность выполнения:

  1. sass-loader
  2. css-loader
  3. style-loader

Загрузчики выполняются справа налево.

Что происходит в цепочке

sass-loader

Преобразует SCSS в обычный CSS.

css-loader

Позволяет импортировать CSS как модуль JavaScript.

style-loader

Вставляет CSS в <style> внутри HTML-страницы.


Раздел module.rules

Все loaders настраиваются внутри module.rules.

Базовая структура:

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                use: 'babel-loader'
            }
        ]
    }
};

Основные свойства rules

test

Определяет файлы, к которым применяется правило.

test: /\.js$/

Пример для SCSS:

test: /\.scss$/

use

Указывает используемые loaders.

Один loader:

use: 'babel-loader'

Несколько loaders:

use: [
    'style-loader',
    'css-loader'
]

exclude

Исключает файлы или каталоги.

Чаще всего исключается node_modules.

exclude: /node_modules/

include

Ограничивает область применения.

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

Babel Loader

Один из самых важных загрузчиков.

Используется для преобразования современного JavaScript.

Установка:

npm install babel-loader @babel/core @babel/preset-env --save-dev

Конфигурация:

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            }
        ]
    }
};

Использование options

Каждый loader может принимать параметры.

Пример:

use: {
    loader: 'babel-loader',
    options: {
        presets: ['@babel/preset-env']
    }
}

options передаёт конфигурацию конкретному загрузчику.


CSS Loader

Позволяет импортировать CSS-файлы.

Установка:

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

Конфигурация:

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader'
                ]
            }
        ]
    }
};

Импорт:

import './styles.css';

Sass Loader

Используется для поддержки SCSS и Sass.

Установка:

npm install sass-loader sass --save-dev

Конфигурация:

module.exports = {
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader'
                ]
            }
        ]
    }
};

Less Loader

Поддержка Less.

Установка:

npm install less-loader less --save-dev

Конфигурация:

{
    test: /\.less$/,
    use: [
        'style-loader',
        'css-loader',
        'less-loader'
    ]
}

TypeScript Loader

Webpack может компилировать TypeScript.

Установка:

npm install typescript ts-loader --save-dev

Конфигурация:

module.exports = {
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: 'ts-loader',
                exclude: /node_modules/
            }
        ]
    }
};

Дополнительно нужен tsconfig.json.


Загрузчики изображений

В Webpack 5 появилась встроенная поддержка ресурсов, однако loaders всё ещё широко используются.

Ранее активно применялся file-loader.

Пример старого подхода:

{
    test: /\.(png|jpg|gif)$/i,
    use: [
        {
            loader: 'file-loader'
        }
    ]
}

Современный вариант:

{
    test: /\.(png|jpg|gif)$/i,
    type: 'asset/resource'
}

Asset Modules

Webpack 5 заменил:

  • file-loader
  • url-loader
  • raw-loader

asset/resource

Создаёт отдельный файл.

{
    test: /\.(png|svg)$/i,
    type: 'asset/resource'
}

asset/inline

Преобразует файл в Base64.

{
    test: /\.svg$/,
    type: 'asset/inline'
}

asset/source

Импортирует содержимое как строку.

{
    test: /\.txt$/,
    type: 'asset/source'
}

asset

Автоматически выбирает стратегию.

{
    test: /\.(png|jpg)$/i,
    type: 'asset'
}

HTML Loader

Позволяет импортировать HTML.

Установка:

npm install html-loader --save-dev

Конфигурация:

{
    test: /\.html$/,
    loader: 'html-loader'
}

Импорт:

import template from './template.html';

CSV Loader

Обработка CSV-файлов.

Установка:

npm install csv-loader --save-dev

Конфигурация:

{
    test: /\.csv$/,
    use: 'csv-loader'
}

XML Loader

Обработка XML.

Установка:

npm install xml-loader --save-dev

Конфигурация:

{
    test: /\.xml$/,
    use: 'xml-loader'
}

Загрузчики и React

Для React обычно используется Babel.

Установка:

npm install babel-loader @babel/preset-react --save-dev

Конфигурация:

{
    test: /\.(js|jsx)$/,
    exclude: /node_modules/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: [
                '@babel/preset-env',
                '@babel/preset-react'
            ]
        }
    }
}

Загрузчики и Vue

Vue использует vue-loader.

Установка:

npm install vue-loader vue-template-compiler --save-dev

Конфигурация:

const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
    module: {
        rules: [
            {
                test: /\.vue$/,
                loader: 'vue-loader'
            }
        ]
    },

    plugins: [
        new VueLoaderPlugin()
    ]
};

Inline Loaders

Loader можно указать прямо в import.

import styles from 'style-loader!css-loader!./styles.css';

Такой подход считается устаревшим и редко используется в современных проектах.

Предпочтительно настраивать loaders через module.rules.


Pre и Post Loaders

Webpack поддерживает стадии обработки.

pre

Выполняется до обычных loaders.

{
    test: /\.js$/,
    enforce: 'pre',
    use: 'eslint-loader'
}

post

Выполняется после остальных loaders.

{
    test: /\.js$/,
    enforce: 'post',
    use: 'some-loader'
}

Loader vs Plugin

Разница принципиальна.

Loader

Преобразует конкретный файл.

Примеры:

  • Babel Loader
  • Sass Loader
  • CSS Loader

Plugin

Воздействует на процесс сборки целиком.

Примеры:

  • HtmlWebpackPlugin
  • MiniCssExtractPlugin
  • DefinePlugin

Порядок выполнения loaders

Важно понимать последовательность.

Конфигурация:

use: [
    'style-loader',
    'css-loader',
    'sass-loader'
]

Выполнение:

sass-loader
↓
css-loader
↓
style-loader

То есть Webpack идёт справа налево.


Использование объекта вместо строки

Короткая форма:

use: 'babel-loader'

Полная форма:

use: {
    loader: 'babel-loader',
    options: {
        presets: ['@babel/preset-env']
    }
}

Полная форма используется чаще, поскольку позволяет задавать параметры.


Массив объектов loaders

Каждый loader может иметь собственные options.

{
    test: /\.scss$/,
    use: [
        {
            loader: 'style-loader'
        },
        {
            loader: 'css-loader',
            options: {
                modules: true
            }
        },
        {
            loader: 'sass-loader'
        }
    ]
}

CSS Modules

Позволяют локализовать CSS-классы.

Конфигурация:

{
    test: /\.css$/,
    use: [
        'style-loader',
        {
            loader: 'css-loader',
            options: {
                modules: true
            }
        }
    ]
}

Использование:

import styles from './Button.module.css';

button.className = styles.primary;

Webpack создаёт уникальные имена классов.


Source Maps и loaders

Для корректной отладки loaders должны поддерживать source maps.

Пример:

{
    loader: 'sass-loader',
    options: {
        sourceMap: true
    }
}

Обычно source maps включаются во всех связанных loaders.


Производительность loaders

Большое количество loaders замедляет сборку.

Основные причины:

  • транспиляция;
  • минификация;
  • обработка source maps;
  • сложные AST-преобразования.

Оптимизация loaders

Исключение node_modules

exclude: /node_modules/

Одна из важнейших оптимизаций.


Использование cache-loader

Позволяет кешировать результаты.

npm install cache-loader --save-dev

Пример:

{
    test: /\.js$/,
    use: [
        'cache-loader',
        'babel-loader'
    ]
}

thread-loader

Параллельная обработка.

npm install thread-loader --save-dev

Пример:

{
    test: /\.js$/,
    use: [
        'thread-loader',
        'babel-loader'
    ]
}

Написание собственного loader

Loader — обычная Node.js-функция.

Простейший пример:

module.exports = function(source) {
    return source.replace('Hello', 'Hi');
};

Использование собственного loader

{
    test: /\.txt$/,
    use: path.resolve(__dirname, 'loaders/my-loader.js')
}

Асинхронные loaders

Webpack поддерживает асинхронную обработку.

Пример:

module.exports = function(source) {
    const callback = this.async();

    setTimeout(() => {
        callback(null, source);
    }, 1000);
};

Контекст loader

Внутри loader доступен объект this.

Полезные свойства:

Свойство Назначение
this.resourcePath Путь к файлу
this.query Параметры
this.async() Асинхронный режим
this.emitFile() Создание файлов

Передача параметров loader

Пример:

{
    loader: 'babel-loader',
    options: {
        cacheDirectory: true
    }
}

Внутри loader параметры доступны через:

this.getOptions()

Pitching Loader

Специальный метод pitch.

module.exports.pitch = function() {
    console.log('pitch');
};

module.exports = function(source) {
    return source;
};

pitch выполняется слева направо, а обычные loaders — справа налево.


Типичная цепочка обработки стилей

SCSS:

SCSS
↓ sass-loader
CSS
↓ css-loader
JS Module
↓ style-loader
STYLE tag

Типичные ошибки loaders

Неверный порядок

Частая проблема:

use: [
    'sass-loader',
    'css-loader'
]

Такой порядок неправильный.


Отсутствие зависимостей

sass-loader требует установленного sass.


Конфликт loaders

Некоторые loaders несовместимы между собой или требуют определённой последовательности.


MiniCssExtractPlugin вместо style-loader

В production чаще используется извлечение CSS в отдельные файлы.

Установка:

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

Конфигурация:

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

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader'
                ]
            }
        ]
    },

    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css'
        })
    ]
};

Разделение конфигураций для development и production

Часто loaders отличаются в зависимости от режима.

Development

use: [
    'style-loader',
    'css-loader'
]

Production

use: [
    MiniCssExtractPlugin.loader,
    'css-loader'
]

Loader Resolution

Webpack ищет loaders:

  1. в node_modules;
  2. по абсолютному пути;
  3. в resolveLoader.

Пример:

resolveLoader: {
    modules: ['node_modules', 'custom-loaders']
}

Raw Loader

Позволяет импортировать файл как строку.

Ранее:

{
    test: /\.txt$/,
    use: 'raw-loader'
}

Современный вариант:

{
    test: /\.txt$/,
    type: 'asset/source'
}

URL Loader

Ранее использовался для inline Base64.

{
    test: /\.(png|jpg)$/i,
    use: {
        loader: 'url-loader',
        options: {
            lim it: 8192
        }
    }
}

В Webpack 5 заменён Asset Modules.


Архитектурная роль loaders

Loaders являются фундаментом экосистемы Webpack.

Именно они позволяют:

  • использовать современные стандарты JavaScript;
  • внедрять CSS в JavaScript-модули;
  • работать с TypeScript;
  • подключать изображения;
  • использовать JSX;
  • интегрировать Sass и Less;
  • строить сложные frontend-конвейеры.

Без loaders Webpack был бы лишь упаковщиком JavaScript-файлов.