В системе загрузчиков Webpack каждый loader может содержать не только
основную функцию преобразования модуля, но и дополнительную стадию
выполнения — pitch-фазу. Она реализуется через метод pitch,
который позволяет перехватывать процесс загрузки до того, как будет
вызвана основная функция цепочки загрузчиков.
Pitch-фаза используется для управления порядком выполнения, оптимизации цепочки loader’ов и даже полного пропуска последующих этапов обработки.
Webpack обрабатывает загрузчики в два этапа:
Для конфигурации:
module: {
rules: [
{
test: /\.txt$/,
use: ['loader-a', 'loader-b', 'loader-c']
}
]
}
Фактический порядок выполнения выглядит так:
loader-a → loader-b → loader-cloader-c → loader-b → loader-aКаждый loader может экспортировать pitch:
module.exports = function (source) {
return source;
};
module.exports.pitch = function (
remainingRequest,
precedingRequest,
data
) {
// логика pitch
};
Параметры:
Ключевая особенность pitch-фазы — возможность прервать дальнейшее выполнение цепочки.
Если pitch возвращает значение, Webpack:
Пример:
module.exports.pitch = function () {
return 'result-from-pitch';
};
В этом случае normal-фаза всех последующих loader’ов не будет вызвана.
Pitch-фаза работает как система раннего выхода.
Рассмотрим цепочку:
use: ['a', 'b', 'c']
Если loader b возвращает значение в pitch,
происходит следующее:
a.pitch выполняетсяb.pitch выполняется и возвращает результатc.pitch не вызываетсяb, затем aLoader c полностью исключается из процесса.
module.exports.pitch = function (remainingRequest) {
if (remainingRequest.includes('special')) {
return `export default "optimized path"`;
}
};
Здесь loader может заменить всю цепочку обработки, если выполняется определённое условие.
remainingRequest содержит строку вида:
/path/to/loader-b.js!/path/to/loader-c.js!/src/file.txt
Это позволяет:
Пример:
module.exports.pitch = function (remainingRequest) {
const request = remainingRequest.split('!').pop();
if (request.endsWith('.txt')) {
return `module.exports = ${JSON.stringify(request)}`;
}
};
precedingRequest содержит уже обработанную часть
цепочки:
/path/to/loader-a.js!
Это используется для:
Webpack предоставляет общий объект data, который
сохраняется между pitch и normal фазой одного loader’а.
Пример использования:
module.exports.pitch = function (remainingRequest, precedingRequest, data) {
data.startTime = Date.now();
};
module.exports = function (source) {
const duration = Date.now() - this.data.startTime;
return source + `\n// processed in ${duration}ms`;
};
Особенности:
Если pitch возвращает undefined, выполнение продолжается
стандартным образом:
Если pitch возвращает строку или Buffer:
Pitch-фаза может быть синхронной или асинхронной:
module.exports.pitch = async function () {
const data = await fetchSomething();
return `export default ${JSON.stringify(data)}`;
};
Однако в классическом Webpack loader API предпочтение отдаётся callback-стилю:
module.exports.pitch = function () {
const callback = this.async();
setTimeout(() => {
callback(null, 'export default "async pitch"');
}, 100);
};
Pitch используется в сценариях:
const cache = new Map();
module.exports.pitch = function (remainingRequest) {
if (cache.has(remainingRequest)) {
return cache.get(remainingRequest);
}
};
module.exports = function (source) {
const result = source.toUpperCase();
cache.set(this.resourcePath, result);
return result;
};
Важно учитывать:
Часто встречающиеся проблемы:
Pitch-фаза фактически превращает loader в управляемый фильтр цепочки, позволяя:
Это делает систему loader’ов не просто последовательностью функций, а управляемым графом исполнения с возможностью раннего завершения и ветвления логики.