Загрузчики в Webpack представляют собой функции, которые преобразуют содержимое модулей перед тем, как они попадут в граф зависимостей и итоговый бандл. Каждый загрузчик выполняется в цепочке (pipeline), где результат одного становится входом для следующего.
По характеру выполнения загрузчики делятся на два типа:
Различие между ними определяется способом возврата результата обработки модуля и управлением потоком выполнения внутри сборщика.
Webpack по умолчанию предполагает синхронное выполнение загрузчиков, если не указано обратное.
Синхронный загрузчик возвращает результат обработки напрямую через
return. Выполнение цепочки при этом происходит
последовательно, без ожидания внешних операций.
Сигнатура синхронного загрузчика:
module.exports = function (source) {
return source;
};
Загрузчик получает на вход строку или Buffer, выполняет преобразование и сразу возвращает результат.
module.exports = function (source) {
const transformed = source.replaceAll('foo', 'bar');
return transformed;
};
Такой загрузчик выполняется мгновенно и не блокирует поток управления Webpack.
Синхронные загрузчики:
this.async()returnWebpack обрабатывает их как чистые функции преобразования.
Ошибки выбрасываются через throw:
module.exports = function (source) {
if (!source) {
throw new Error('Пустой модуль');
}
return source;
};
Webpack перехватывает исключение и останавливает сборку для текущего модуля.
Синхронный режим используется в случаях, когда:
Примеры:
Асинхронные загрузчики используются, когда обработка требует ожидания внешних операций:
Webpack предоставляет специальный механизм —
this.async().
Асинхронный загрузчик получает callback через
this.async():
module.exports = function (source) {
const callback = this.async();
setTimeout(() => {
const result = source.toUpperCase();
callback(null, result);
}, 100);
};
callback(err, result, sourceMap, meta);
Параметры:
err — ошибка (или null)result — преобразованный кодsourceMap — sourcemap (опционально)meta — дополнительные данныеconst fs = require('fs');
module.exports = function (source) {
const callback = this.async();
fs.readFile('./template.txt', 'utf-8', (err, template) => {
if (err) return callback(err);
const result = template.replace('{{content}}', source);
callback(null, result);
});
};
Webpack поддерживает возврат Promise, что делает
асинхронный код чище.
module.exports = function (source) {
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
const result = source.trim();
resolve(result);
} catch (e) {
reject(e);
}
}, 50);
});
};
Если загрузчик возвращает Promise, Webpack автоматически ожидает его завершения.
module.exports = async function (source) {
const data = await Promise.resolve(source.toLowerCase());
return data;
};
Асинхронная функция автоматически превращается в Promise-загрузчик.
Синхронный:
returnАсинхронный:
Цепочка загрузчиков выполняется справа налево, но тип выполнения влияет на управление потоком:
Синхронные и асинхронные загрузчики могут комбинироваться:
module: {
rules: [
{
test: /\.txt$/,
use: [
'sync-loader',
'async-loader',
'final-loader'
]
}
]
}
В такой цепочке Webpack автоматически переключается между режимами исполнения в зависимости от загрузчика.
Объект this внутри загрузчика предоставляет API
Webpack:
this.async()this.callback()this.cacheable()this.emitFile()this.resourcePathПри асинхронной работе особенно важен метод:
const callback = this.async();
Он переводит загрузчик в асинхронный режим. Если
this.async() не вызван, Webpack считает загрузчик
синхронным.
В асинхронных загрузчиках ошибки передаются через callback или Promise:
callback(new Error('Ошибка обработки'), null);
return Promise.reject(new Error('Ошибка'));
Webpack корректно останавливает сборку текущего модуля и выводит ошибку в лог.
Асинхронные загрузчики не обязательно ускоряют сборку. Их цель — не блокировать поток выполнения при ожидании I/O.
Характер поведения:
Загрузчики имеют две фазы:
Асинхронность может использоваться и в pitch-фазе:
module.exports.pitch = function () {
const callback = this.async();
setTimeout(() => {
callback(null, 'data from pitch');
}, 10);
};
Pitch-фаза может остановить выполнение цепочки до перехода к normal-фазе других загрузчиков.
Асинхронные операции часто влияют на кешируемость:
module.exports = function (source) {
this.cacheable(true);
return new Promise((resolve) => {
setTimeout(() => resolve(source), 10);
});
};
Webpack кеширует результат, если загрузчик объявлен как кешируемый и не зависит от внешнего состояния.
Выбор между синхронным и асинхронным режимом определяется характером работы:
Загрузчик может динамически выбирать режим выполнения:
module.exports = function (source) {
if (source.includes('async')) {
const callback = this.async();
setTimeout(() => {
callback(null, source);
}, 20);
return;
}
return source;
};
Такой подход позволяет адаптировать поведение под входные данные, сохраняя совместимость с Webpack pipeline.