Интерфейс загрузчика
Загрузчик — это просто JavaScript-модуль, который экспортирует функцию. Загрузчик исполняемых файлов вызывает эту функцию и передает ей результат предыдущего загрузчика или файла-ресурса. Контекст функции заполняется webpack и загрузчиком исполняемых файлов полезными методами, которые позволяют загрузчику (среди прочего) изменить стиль вызова на асинхронный или получить параметры запроса.
Первому загрузчику передаётся один аргумент: содержимое файла-ресурса. Компилятор ожидает результат от последнего загрузчика. Результат должен быть String или Buffer (преобразованный в строку), представляющий JavaScript-исходный код модуля. Также может быть передан необязательный результат SourceMap (в виде JSON-объекта).
Один результат может быть возвращён в режиме синхронного выполнения. Для возвращения нескольких результатов необходимо вызвать this.callback(). В асинхронном режиме необходимо вызвать this.async(), чтобы указать загрузчику исполняемых файлов ожидать асинхронного результата. Он возвращает this.callback(). Затем загрузчик должен вернуть undefined и вызвать этот коллбэк.
Примеры
В следующих разделах приведены некоторые базовые примеры различных типов загрузчиков. Обратите внимание, что параметры map и meta являются необязательными, см. this.callback ниже.
Синхронные загрузчики
Для возвращения преобразованного content в синхронном режиме можно использовать return или this.callback.
sync-loader.js
module.exports = function(content, map, meta) {
return someSyncOperation(content);
}; Метод this.callback более гибкий, так как позволяет передавать несколько аргументов, в отличие от просто content.
sync-loader-with-multiple-results.js
module.exports = function(content, map, meta) {
this.callback(null, someSyncOperation(content), map, meta);
return; // always return undefined when calling callback()
}; Асинхронные загрузчики
Для асинхронных загрузчиков используется this.async для получения функции callback:
async-loader.js
module.exports = function(content, map, meta) {
var callback = this.async();
someAsyncOperation(content, function(err, result) {
if (err) return callback(err);
callback(null, result, map, meta);
});
}; async-loader-with-multiple-results.js
module.exports = function(content, map, meta) {
var callback = this.async();
someAsyncOperation(content, function(err, result, sourceMaps, meta) {
if (err) return callback(err);
callback(null, result, sourceMaps, meta);
});
}; Загрузчики изначально были разработаны для работы в синхронных конвейерах загрузки, таких как Node.js (используя enhanced-require), и асинхронных конвейерах, как в webpack. Однако, поскольку дорогостоящие синхронные вычисления — плохая идея в однопоточной среде, такой как Node.js, мы рекомендуем делать ваши загрузчики асинхронными, если это возможно. Синхронные загрузчики подходят, если объём вычислений незначителен.
"Необработанный" загрузчик
По умолчанию файл-ресурс преобразуется в строку UTF-8 и передаётся загрузчику. Установив флаг raw, загрузчик получит Buffer в необработанном виде. Каждый загрузчик может возвращать результат в виде String или Buffer. Компилятор преобразует их между загрузчиками.
raw-loader.js
module.exports = function(content) {
assert(content instanceof Buffer);
return someSyncOperation(content);
// return value can be a `Buffer` too
// This is also allowed if loader is not "raw"
};
module.exports.raw = true; Загрузчик "Pitching"
Загрузчики всегда вызываются справа налево. В некоторых случаях загрузчик интересуется только метаданными запроса и может проигнорировать результаты предыдущего загрузчика. Метод pitch загрузчиков вызывается слева направо перед фактическим выполнением загрузчиков (справа налево).
Загрузчики могут быть добавлены в запросы встроены и отключены с помощью встроенных префиксов, что повлияет на порядок их "подготовки" и выполнения. Подробнее см.
Rule.enforce.
Для следующей конфигурации use:
module.exports = {
//...
module: {
rules: [
{
//...
use: [
'a-loader',
'b-loader',
'c-loader'
]
}
]
}
}; Эти шаги будут выполнены:
|- a-loader `pitch`
|- b-loader `pitch`
|- c-loader `pitch`
|- requested module is picked up as a dependency
|- c-loader normal execution
|- b-loader normal execution
|- a-loader normal execution И зачем загрузчику использовать фазу "подготовки"?
Во-первых, data, переданное методу pitch в фазе выполнения, также доступно через this.data и может быть полезно для захвата и обмена информацией из более ранней части цикла.
module.exports = function(content) {
return someSyncOperation(content, this.data.value);
};
module.exports.pitch = function(remainingRequest, precedingRequest, data) {
data.value = 42;
}; Во-вторых, если загрузчик возвращает результат в методе pitch, процесс разворачивается и пропускает оставшиеся загрузчики. В нашем примере выше, если метод b-loader загрузчика pitch вернул что-то:
module.exports = function(content) {
return someSyncOperation(content);
};
module.exports.pitch = function(remainingRequest, precedingRequest, data) {
if (someCondition()) {
return 'module.exports = require(' + JSON.stringify('-!' + remainingRequest) + ');';
}
}; Вышеуказанные шаги сократятся до:
|- a-loader `pitch` |- b-loader `pitch` returns a module |- a-loader normal execution
См. bundle-loader для хорошего примера того, как этот процесс может быть использован более осмысленно.
Контекст загрузчика
Контекст загрузчика представляет свойства, доступные внутри загрузчика, назначенного свойству this.
Приведенном примере используется следующий вызов require:
В /abc/file.js:
require('./loader1?xyz!loader2!./resource?rrr');
this.version
Версия API загрузчика. В настоящее время 2. Это полезно для обеспечения обратной совместимости. Используя версию, можно указать пользовательскую логику или обходные пути для несовместимых изменений.
this.context
Директория модуля. Может использоваться в качестве контекста для разрешения других вещей.
В примере: /abc, потому что resource.js находится в этой директории
this.rootContext
Начиная с webpack 4, ранее this.options.context предоставляется как this.rootContext.
this.request
Строка разрешённого запроса.
В примере: "/abc/loader1.js?xyz!/abc/node_modules/loader2/index.js!/abc/resource.js?rrr"
this.query
- Если загрузчик был настроен с объектом
options, он будет указывать на этот объект. - Если у загрузчика нет
options, но он был вызван со строкой запроса, то это будет строка, начинающаяся с?.
Используйте метод
getOptionsизloader-utilsдля извлечения заданных опций загрузчика.
this.callback
Функция, которая может быть вызвана синхронно или асинхронно для возвращения нескольких результатов. Ожидаемые аргументы:
this.callback( err: Error | null, content: string | Buffer, sourceMap?: SourceMap, meta?: any );
- Первый аргумент должен быть
Errorилиnull - Второй аргумент — это
stringилиBuffer. - Необязательно: третий аргумент должен быть картой сопоставления, которая может быть обработана этим модулем .
- Необязательно: четвёртый параметр, игнорируемый webpack, может быть любым (например, какие-либо метаданные).
Полезно передать абстрактное синтаксическое дерево (AST), например,
ESTree, в качестве четвёртого аргумента (meta). Это ускорит время сборки, если вы хотите использовать общие AST между загрузчиками.
В случае вызова этой функции, вы должны вернуть undefined, чтобы избежать неоднозначных результатов загрузчика.
this.async
Уведомляет загрузчик исполняемых файлов, что загрузчик намерен выполнить обратный вызов асинхронно. Возвращает this.callback.
this.data
Объект данных, обмениваемый между фазами "подготовки" и обычной фазой.
this.cacheable
Функция, которая устанавливает флаг cacheable:
cacheable(flag = true: boolean)
По умолчанию результаты загрузчика помечаются как кэшируемые. Вызовите этот метод, передав false, чтобы результат загрузчика не был кэшируемым.
Кэшируемый загрузчик должен иметь детерминированный результат, когда входные данные и зависимости не изменились. Это означает, что у загрузчика не должно быть зависимостей, кроме тех, которые указаны с помощью this.addDependency.
this.loaders
Массив всех загрузчиков. Он изменяемый в фазе подготовки.
loaders = [{request: string, path: string, query: string, module: function}] В примере:
[
{
request: '/abc/loader1.js?xyz',
path: '/abc/loader1.js',
query: '?xyz',
module: [Function]
},
{
request: '/abc/node_modules/loader2/index.js',
path: '/abc/node_modules/loader2/index.js',
query: '',
module: [Function]
}
];
this.loaderIndex
Индекс текущего загрузчика в массиве загрузчиков.
В примере: в загрузчике 1: 0, в загрузчике 2: 1
this.resource
Часть запроса "ресурс", включая запрос.
В примере: "/abc/resource.js?rrr"
this.resourcePath
Файл-ресурс.
В примере: "/abc/resource.js"
this.resourceQuery
Запрос ресурса.
В примере: "?rrr"
this.target
Целевая точка компиляции. Передаётся из опций конфигурации.
Примеры значений: "web", "node"
this.webpack
Этот булевый флаг устанавливается в true, когда это компилируется webpack.
Загрузчики изначально были разработаны для работы также как преобразования Babel. Поэтому, если вы пишете загрузчик, который работает и для того, и для другого, вы можете использовать это свойство, чтобы узнать, есть ли доступ к дополнительным функциям loaderContext и webpack.
this.sourceMap
Указывает, нужно ли генерировать карту сопоставления. Поскольку генерация карт сопоставления может быть дорогостоящей операцией, вы должны проверить, требуются ли карты сопоставления.
this.emitWarning
emitWarning(warning: Error)
Вывести предупреждение, которое будет отображено в выводе, например:
WARNING in ./src/lib.js (./src/loader.js!./src/lib.js) Module Warning (from ./src/loader.js): Here is a Warning! @ ./src/index.js 1:0-25
Обратите внимание, что предупреждения не будут отображены, если
stats.warningsустановлено вfalse, или используется какое-либо другое значение исключения, например,noneилиerrors-only. См. конфигурацию stats.
this.emitError
emitError(error: Error)
Вывести ошибку, которая также может быть отображена в выводе.
ERROR in ./src/lib.js (./src/loader.js!./src/lib.js) Module Error (from ./src/loader.js): Here is an Error! @ ./src/index.js 1:0-25
В отличие от прямого выброса ошибки, это НЕ прервёт процесс компиляции текущего модуля.
this.loadModule
loadModule(request: string, callback: function(err, source, sourceMap, module))
Разрешает заданный запрос на модуль, применяет все настроенные загрузчики и обращается назад с сгенерированным исходным кодом, картой сопоставления и экземпляром модуля (обычно экземпляром NormalModule). Используйте эту функцию, если вам нужен исходный код другого модуля для генерации результата.
this.resolve
resolve(context: string, request: string, callback: function(err, result: string))
Разрешить запрос, подобный выражению require.
this.addDependency
addDependency(file: string) dependency(file: string) // shortcut
Добавить файл в качестве зависимости результата загрузчика, чтобы сделать их отслеживаемыми. Например, sass-loader, less-loader используют это для повторной компиляции при каждом изменении импортированного css файла.
this.addContextDependency
addContextDependency(directory: string)
Добавить директорию в качестве зависимости результата загрузчика.
this.clearDependencies
clearDependencies()
Удалить все зависимости результата загрузчика, включая начальные зависимости и зависимости других загрузчиков. Рассмотрите использование pitch.
this.emitFile
emitFile(name: string, content: Buffer|string, sourceMap: {...}) Вывести файл. Это специфично для webpack.
this.fs
Доступ к свойству compilation объекта inputFileSystem.
this.mode
Прочитать, в каком mode режиме выполняется webpack.
Возможные значения: 'production', 'development', 'none'
Устаревшие свойства контекста
Использование этих свойств крайне не рекомендуется, так как мы планируем удалить их из контекста. Они все еще перечислены здесь для целей документации.
this.exec
exec(code: string, filename: string)
Выполнить фрагмент кода, как модуль. Смотрите этот комментарий для замены метода, если необходимо.
this.resolveSync
resolveSync(context: string, request: string) -> string
Разрешить запрос, подобный выражению require.
this.value
Передать значения следующему загрузчику. Если вы знаете, что экспортирует ваш результат, если он выполнен как модуль, установите это значение здесь (в виде массива с единственным элементом).
this.inputValue
Передано последним загрузчиком. Если вы хотите выполнить входной аргумент как модуль, рассмотрите чтение этой переменной для сокращения (для производительности).
this.options
Свойство
optionsбыло устаревшим в webpack 3 и удалено в webpack 4.
this.debug
Флажок булевого типа. Устанавливается в режиме отладки.
this.minimize
Указывает, должен ли результат быть сжат.
this._compilation
Неофициально получить доступ к объекту Compilation webpack.
this._compiler
Неофициально получить доступ к объекту Compiler webpack.
this._module
Неофициально получить доступ к объекту Module, который загружается.
Отчет об ошибках
Вы можете сообщать об ошибках изнутри загрузчика:
- Используя this.emitError. Сообщит об ошибках без прерывания компиляции модуля.
- Используя
throw(или другую неперехваченную исключительную ситуацию). Бросок ошибки во время работы загрузчика приведет к ошибке компиляции текущего модуля. - Используя
callback(в асинхронном режиме). Передача ошибки в обратный вызов также приведет к ошибке компиляции модуля.
Например:
./src/index.js
require('./loader!./lib'); Бросок ошибки из загрузчика:
./src/loader.js
module.exports = function(source) {
throw new Error('This is a Fatal Error!');
}; Или передача ошибки в обратный вызов в асинхронном режиме:
./src/loader.js
module.exports = function(source) {
const callback = this.async();
//...
callback(new Error('This is a Fatal Error!'), source);
}; Модуль будет объединен следующим образом:
/***/ "./src/loader.js!./src/lib.js":
/*!************************************!*\
!*** ./src/loader.js!./src/lib.js ***!
\************************************/
/*! no static exports found */
/***/ (function(module, exports) {
throw new Error("Module build failed (from ./src/loader.js):\nError: This is a Fatal Error!\n at Object.module.exports (/workspace/src/loader.js:3:9)");
/***/ }) Затем выходные данные сборки также отобразят ошибку (аналогично this.emitError):
ERROR in ./src/lib.js (./src/loader.js!./src/lib.js)
Module build failed (from ./src/loader.js):
Error: This is a Fatal Error!
at Object.module.exports (/workspace/src/loader.js:2:9)
@ ./src/index.js 1:0-25 Как вы можете видеть ниже, отображается не только сообщение об ошибке, но и подробности о вовлеченных загрузчике и модуле:
- путь к модулю:
ERROR in ./src/lib.js - строка запроса:
(./src/loader.js!./src/lib.js) - путь к загрузчику:
(from ./src/loader.js) - путь к вызывающему объекту:
@ ./src/index.js 1:0-25
Путь к загрузчику в ошибке отображается с версии webpack 4.12
Все ошибки и предупреждения будут записаны в
stats. См. Данные статистики.
Встроенное соответствие ресурсу
В webpack v4 был введен новый синтаксис встроенных запросов. Предварительное добавление <match-resource>!=! к запросу установит matchResource для этого запроса.
Не рекомендуется использовать этот синтаксис в коде приложения. Встроенный синтаксис запросов предназначен только для использования кодом, сгенерированным загрузчиками. Несоблюдение этого правила сделает ваш код специфичным для webpack и нестандартным.
Относительный
matchResourceбудет разрешаться относительно текущего контекста содержащего модуля.
Когда matchResource устанавливается, он используется для сопоставления с module.rules вместо исходного ресурса. Это может быть полезно, если дальнейшие загрузчики должны быть применены к ресурсу или если тип модуля нужно изменить. Также он отображается в статистике и используется для сопоставления Rule.issuer и test в splitChunks.
Пример:
file.js
/* STYLE: body { background: red; } */
console.log('yep'); Загрузчик может преобразовать файл в следующий файл и использовать matchResource для применения правил обработки CSS, указанных пользователем:
file.js (преобразованный загрузчиком)
import './file.js.css!=!extract-style-loader/getStyles!./file.js';
console.log('yep'); Это добавит зависимость от extract-style-loader/getStyles!./file.js и обработает результат как file.js.css. Поскольку module.rules содержит правило, соответствующее /\.css$/, оно будет применено к этой зависимости.
Загрузчик может выглядеть так:
extract-style-loader/index.js
const stringifyRequest = require('loader-utils').stringifyRequest;
const getRemainingRequest = require('loader-utils').getRemainingRequest;
const getStylesLoader = require.resolve('./getStyle');
module.exports = function (source) {
if (STYLES_REGEXP.test(source)) {
source = source.replace(STYLES_REGEXP, '');
const remReq = getRemainingRequest(this);
return `import ${stringifyRequest(`${this.resource}.css!=!${getStylesLoader}!${remReq}`)};${source}`;
}
return source;
}; extract-style-loader/getStyles.js
module.exports = function(source) {
const match = STYLES_REGEXP.match(source);
return match[0];
}; Ведение журнала
API ведения журнала доступен с выпуска webpack 4.37. Когда logging включен в stats configuration и/или когда infrastructure logging включен, загрузчики могут регистрировать сообщения, которые будут выведены в соответствующем формате регистратора (статистика, инфраструктура).
- Загрузчики должны предпочитать использовать
this.getLogger()для ведения журнала, что является сокращением дляcompilation.getLogger()с путем к загрузчику и обработанному файлу. Такой вид ведения журнала сохраняется в статистике и форматируется соответствующим образом. Пользователь webpack может его фильтровать и экспортировать. - Загрузчики могут использовать
this.getLogger('name')для получения независимого регистратора с именем дочернего элемента. Путь к загрузчику и обработанный файл все еще добавляются. - Загрузчики могут использовать специальную логику отработки для обнаружения поддержки ведения журнала
this.getLogger() ? this.getLogger() : consoleдля обеспечения отработки в случае использования более старой версии webpack, не поддерживающей методgetLogger.
© JS Foundation and other contributors
Licensed under the Creative Commons Attribution License 4.0.
https://v4.webpack.js.org/api/loaders