Порядок применения загрузчиков: справа налево, снизу вверх

Механизм применения загрузчиков в Webpack основан на строгом порядке их выполнения, который напрямую влияет на итоговую обработку модулей. Ошибочное понимание этого порядка приводит к некорректной трансформации кода, неожиданным результатам сборки и трудноуловимым багам.


Логика цепочки загрузчиков

Webpack применяет загрузчики как последовательность функций, где каждый следующий loader получает результат работы предыдущего. Эта цепочка формируется в конфигурации правила module.rules.

Ключевой принцип:

загрузчики выполняются справа налево (right-to-left)

Это означает, что последний loader в массиве вызывается первым.

Пример:

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

Фактический порядок выполнения будет следующим:

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

Почему порядок обратный

Webpack строит цепочку в виде композиции функций, аналогичной:

styleLoader(cssLoader(postcssLoader(input)))

Каждый loader оборачивает результат предыдущего, поэтому выполнение происходит изнутри наружу.

Это поведение обусловлено архитектурой потоковой обработки модулей:

  • сначала происходит максимально глубокая трансформация исходного кода
  • затем постепенное “оборачивание” результата в более высокоуровневые представления

Правило снизу вверх в конфигурации

Помимо горизонтального порядка внутри массива use, существует вертикальный порядок применения правил в module.rules.

Webpack проходит по массиву правил сверху вниз, но применяет только те loaders, чьи условия совпали.

Однако внутри одного совпавшего правила действует обратная логика:

  • массив rules → сверху вниз (поиск подходящего rule)
  • массив use → справа налево (применение loaders)

Пример комбинированного поведения

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

Webpack:

  1. Проверяет первое правило (.js)
  2. Если совпало — применяет loader
  3. Переходит ко второму правилу (.css)
  4. Для CSS запускает цепочку loaders справа налево

Расширенная цепочка loaders

Цепочки могут быть значительно сложнее:

use: [
  'style-loader',
  {
    loader: 'css-loader',
    options: {
      modules: true
    }
  },
  'postcss-loader',
  'sass-loader'
]

Фактический порядок:

  1. sass-loader — преобразует SCSS/SASS в CSS
  2. postcss-loader — постобработка (автопрефиксы, плагины)
  3. css-loader — интерпретация @import и url()
  4. style-loader — внедрение стилей в DOM

Важное различие: pitch и normal loaders

Webpack loaders могут содержать два типа фаз:

  • normal phase — основная обработка модуля
  • pitch phase — предварительная обработка

И здесь порядок становится ещё сложнее:

Pitch выполняется слева направо

Если loaders имеют pitch-методы, Webpack сначала проходит по ним:

use: ['a-loader', 'b-loader', 'c-loader']

Порядок pitch-фазы:

  1. a-loader.pitch
  2. b-loader.pitch
  3. c-loader.pitch

Normal выполняется справа налево

После pitch-фазы:

  1. c-loader
  2. b-loader
  3. a-loader

Когда pitch прерывает цепочку

Pitch loader может вернуть значение, и тогда дальнейшая цепочка normal loaders не выполняется.

module.exports.pitch = function (remainingRequest) {
  return "export default 'cached result'";
};

В этом случае:

  • normal loaders пропускаются
  • результат берётся напрямую из pitch

Это позволяет реализовывать кэширование и оптимизации на уровне загрузчиков.


Влияние order на итоговую сборку

Неправильное расположение loaders может привести к критическим ошибкам:

Пример ошибки порядка

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

Проблема:

  • css-loader возвращает JS-модуль
  • style-loader ожидает CSS-строку

Итог: некорректная интерпретация и падение сборки

Правильный порядок:

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

Логика восприятия цепочки

Удобная ментальная модель:

  • массив use читается как список этапов сборки
  • выполнение происходит как стек (LIFO — last in, first out)

Можно представить так:

input → loader3 → loader2 → loader1 → output

где loader3 стоит последним в массиве.


Разделение ответственности loaders

Правильная организация порядка позволяет разделять ответственность:

  • трансформация синтаксиса (SASS, TypeScript)
  • постобработка (PostCSS, Babel plugins)
  • интерпретация зависимостей (css-loader)
  • интеграция в runtime (style-loader)

Каждый этап должен быть строго на своём месте в цепочке.


Нюансы с enforce: pre и post

Webpack позволяет дополнительно управлять порядком через enforce:

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

Порядок становится:

  1. eslint-loader (pre)
  2. babel-loader (normal)

Существует также:

  • enforce: 'post' — выполняется после всех обычных loaders

Комбинация всех уровней порядка

Итоговая система приоритета:

  1. enforce: pre — сверху приоритет
  2. обычные rules — сверху вниз по массиву
  3. внутри rule — loaders справа налево
  4. enforce: post — выполняется последним
  5. внутри loaders — pitch слева направо, normal справа налево

Практическая интерпретация цепочек

Для анализа сложных конфигураций удобно мысленно разворачивать цепочку:

use: [
  'loader-a',
  'loader-b',
  'loader-c'
]

в:

loader-c → loader-b → loader-a

И затем учитывать:

  • pitch-фазу (если есть)
  • post/pre-правила
  • порядок rules

Типовые ошибки из-за порядка

  1. SASS после CSS loader

    • нарушает преобразование препроцессора
  2. Babel после Webpack asset loader

    • приводит к попытке транспиляции бинарных данных
  3. style-loader в начале цепочки

    • ломает runtime-инъекцию стилей

Вывод логики порядка как системы

Порядок loaders в Webpack — это не просто массив конфигурации, а строго определённая модель композиции функций, где:

  • массив описывает структуру
  • выполнение строится как стек
  • дополнительные механизмы (pitch, enforce) изменяют базовую последовательность
  • итоговый результат полностью зависит от корректной ориентации цепочки