Webpack Loaders

Зачем нужно

Loaders — это трансформаторы файлов в Webpack. Webpack из коробки понимает только JS и JSON. Loaders позволяют импортировать CSS, изображения, TypeScript, SCSS и вообще всё что угодно — каждый loader конвертирует файл в модуль, понятный Webpack.

Где используется

  • Транспиляция JS/TS (babel-loader, ts-loader)
  • Обработка стилей (css-loader, sass-loader)
  • Загрузка ресурсов (file-loader, url-loader)
  • Линтинг (eslint-loader)
  • Шаблоны (html-loader, pug-loader)

Как работают loaders

Файл → Loader 1 → Loader 2 → Loader 3 → Webpack

Порядок выполнения: СПРАВА → НАЛЕВО (снизу → вверх)

use: ['style-loader', 'css-loader', 'postcss-loader']
       ▲               ▲                ▲
       3               2                1
// webpack.config.js — базовая структура
module.exports = {
  module: {
    rules: [
      {
        test: /\.ext$/,           // RegExp для файлов
        exclude: /node_modules/,  // Что исключить
        use: 'loader-name',       // Один loader
        // или
        use: ['loader-a', 'loader-b'],  // Цепочка
        // или с опциями
        use: [
          {
            loader: 'loader-name',
            options: { /* настройки */ },
          },
        ],
      },
    ],
  },
};

babel-loader

Транспилирует современный JavaScript (ES6+) и JSX в код, понятный старым браузерам:

npm install --save-dev babel-loader @babel/core @babel/preset-env
{
  test: /\.jsx?$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: [
        ['@babel/preset-env', {
          targets: '> 0.25%, not dead',  // Браузеры
          useBuiltIns: 'usage',          // Полифилы по необходимости
          corejs: 3,
        }],
      ],
      plugins: [
        '@babel/plugin-proposal-class-properties',
      ],
      cacheDirectory: true, // Кэширование для скорости
    },
  },
}
// .babelrc — альтернативно вынести конфиг
{
  "presets": [
    ["@babel/preset-env", { "targets": "> 0.25%, not dead" }],
    "@babel/preset-react"  // Для JSX
  ]
}

css-loader и style-loader

npm install --save-dev css-loader style-loader
// css-loader — разбирает CSS, обрабатывает @import и url
// style-loader — вставляет CSS в DOM через <style> тег

{
  test: /\.css$/,
  use: ['style-loader', 'css-loader'],
  // 1. css-loader парсит CSS
  // 2. style-loader вставляет в <style>
}

CSS Modules

{
  test: /\.module\.css$/,
  use: [
    'style-loader',
    {
      loader: 'css-loader',
      options: {
        modules: {
          localIdentName: '[name]__[local]--[hash:base64:5]',
        },
      },
    },
  ],
}
// Использование
import styles from './Button.module.css';
// styles = { primary: 'Button__primary--a3b2c', active: '...' }
element.className = styles.primary;

postcss-loader

npm install --save-dev postcss-loader postcss autoprefixer
{
  test: /\.css$/,
  use: [
    'style-loader',
    'css-loader',
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: [
            'autoprefixer',  // Добавляет вендорные префиксы
          ],
        },
      },
    },
  ],
}

sass-loader (SCSS)

npm install --save-dev sass-loader sass
{
  test: /\.scss$/,
  use: [
    'style-loader',
    'css-loader',
    'postcss-loader',
    'sass-loader',    // Компилирует SCSS → CSS
  ],
}

ts-loader

Компилирует TypeScript:

npm install --save-dev ts-loader typescript
{
  test: /\.tsx?$/,
  exclude: /node_modules/,
  use: 'ts-loader',
}
// Альтернатива — babel-loader с @babel/preset-typescript
// Быстрее, но без проверки типов
{
  test: /\.tsx?$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: [
        '@babel/preset-env',
        '@babel/preset-typescript',
      ],
    },
  },
}

Работа с ресурсами (Webpack 5)

В Webpack 5 встроенная обработка ресурсов заменяет file-loader, url-loader, raw-loader:

// asset/resource — файл копируется в output (как file-loader)
{
  test: /\.(png|jpg|gif|svg)$/,
  type: 'asset/resource',
  generator: {
    filename: 'images/[name].[hash:8][ext]',
  },
}

// asset/inline — файл встраивается как base64 (как url-loader)
{
  test: /\.svg$/,
  type: 'asset/inline',
}

// asset — автовыбор: маленькие → inline, большие → resource
{
  test: /\.(png|jpg|gif)$/,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 8 * 1024, // 8kb
    },
  },
}

// asset/source — содержимое как строка (как raw-loader)
{
  test: /\.txt$/,
  type: 'asset/source',
}

Устаревшие loaders (Webpack 4)

// file-loader — копирует файл, возвращает URL
{
  test: /\.(png|jpg|gif)$/,
  use: {
    loader: 'file-loader',
    options: { name: '[name].[hash:8].[ext]', outputPath: 'images/' },
  },
}

// url-loader — маленькие файлы → base64
{
  test: /\.(png|jpg|gif)$/,
  use: {
    loader: 'url-loader',
    options: { limit: 8192, fallback: 'file-loader' },
  },
}

html-loader

npm install --save-dev html-loader
{
  test: /\.html$/,
  use: {
    loader: 'html-loader',
    options: {
      // Обрабатывает src в img, href в link и т.д.
      sources: true,
      minimize: true,
    },
  },
}

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

// my-loader.js
module.exports = function(source) {
  // source — содержимое файла как строка
  // this.query — опции из конфига

  // Пример: заменить все TODO на DONE
  const result = source.replace(/TODO/g, 'DONE');

  // Вернуть трансформированный код
  return result;
};

// Асинхронный loader
module.exports = function(source) {
  const callback = this.async;

  someAsyncOperation(source)
    .then(result => callback(null, result))
    .catch(err => callback(err));
};
// Подключение в конфиге
{
  test: /\.js$/,
  use: path.resolve('./my-loader.js'),
}

Частые ошибки

  1. Неправильный порядок loaders['css-loader', 'style-loader'] вместо ['style-loader', 'css-loader']
  2. Не исключают node_modulesbabel-loader обрабатывает тысячи файлов из зависимостей
  3. file-loader в Webpack 5 — file-loader, url-loader, raw-loader устарели, использовать type: 'asset'
  4. Конфликт CSS Modules — обычный CSS и CSS Modules нужны разные rules
  5. Нет cacheDirectory — babel-loader без кэша медленно перекомпилирует при каждом изменении

Практика

  1. Настроить babel-loader для транспиляции ES6+ → ES5
  2. Подключить css-loader + style-loader, импортировать CSS в JS
  3. Настроить CSS Modules с уникальными именами классов
  4. Добавить sass-loader для SCSS
  5. Настроить обработку изображений через type: 'asset'

Связанные темы

  • Webpack — общая настройка Webpack
  • Webpack plugins — расширение функциональности
  • Vite — альтернатива без loaders

Ресурсы