Интерфейс загрузчика
Загрузчик — это JavaScript-модуль, который экспортирует функцию. Загрузчик вызывается загрузчиком и получает на вход результат предыдущего загрузчика или ресурсный файл. Контекст функции заполняется webpack и загрузчиком с полезными методами, позволяющими, среди прочего, изменить стиль вызова на асинхронный или получить параметры запроса.
Первый загрузчик получает один аргумент: содержимое ресурсного файла. Компилятор ожидает результат от последнего загрузчика. Результат должен быть String или Buffer (который преобразуется в строку), представляющий JavaScript-код модуля. Также может быть передан необязательный результат SourceMap (в виде JSON-объекта).
Единственный результат может быть возвращён в синхронном режиме. Для нескольких результатов необходимо вызвать this.callback(). В асинхронном режиме необходимо вызвать this.async(), чтобы указать загрузчику ожидать асинхронного результата. Возвращается this.callback(). Затем загрузчик должен вернуть undefined и вызвать этот коллбэк.
/**
*
* @param {string|Buffer} content Content of the resource file
* @param {object} [map] SourceMap data consumable by https://github.com/mozilla/source-map
* @param {any} [meta] Meta data, could be anything
*/
function webpackLoader(content, map, meta) {
// code of your webpack loader
} Примеры
В следующих разделах представлены некоторые базовые примеры различных типов загрузчиков. Обратите внимание, что параметры 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);
});
}; "Сырой" загрузчик
По умолчанию, ресурсный файл преобразуется в строку UTF-8 и передаётся загрузчику. Установив флаг raw в значение true, загрузчик получит сырой 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; Фаза предварительной обработки загрузчика
Загрузчики всегда вызываются справа налево. В некоторых случаях загрузчику требуется только метаданные запроса, и он может игнорировать результаты предыдущего загрузчика. Метод pitch загрузчиков вызывается слева направо перед фактическим выполнением загрузчиков (справа налево).
Для следующей конфигурации 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
Контекст загрузчика
Контекст загрузчика представляет собой свойства, доступные внутри загрузчика, присвоенные свойству this.
Пример для контекста загрузчика
Рассмотрим следующий пример, в котором используется вызов require:
В /abc/file.js:
require('./loader1?xyz!loader2!./resource?rrr'); this.addContextDependency
addContextDependency(directory: string)
Добавить директорию в качестве зависимости результата загрузчика.
this.addDependency
addDependency(file: string) dependency(file: string) // shortcut
Добавить существующий файл в качестве зависимости результата загрузчика, чтобы сделать его наблюдаемым. Например, sass-loader, less-loader используют это для перекомпиляции при изменении любого импортированного css файла.
this.addMissingDependency
addMissingDependency(file: string)
Добавить несуществующий файл в качестве зависимости результата загрузчика, чтобы сделать его наблюдаемым. Аналогично addDependency, но обрабатывает создание файлов во время компиляции перед корректной привязкой наблюдателей.
this.async
Указывает загрузчику на асинхронный вызов коллбэка. Возвращает this.callback.
this.cacheable
Функция, которая устанавливает флаг кэшируемости:
cacheable(flag = true: boolean)
По умолчанию, результаты загрузчиков помечаются как кэшируемые. Вызовите этот метод, передав false, чтобы результат загрузчика не был кэшируемым.
Кэшируемый загрузчик должен иметь детерминированный результат, когда входные данные и зависимости не изменились. Это означает, что у загрузчика не должно быть зависимостей, кроме тех, что указаны с помощью this.addDependency.
this.callback
Функция, которая может быть вызвана синхронно или асинхронно для возврата нескольких результатов. Ожидаемые аргументы:
this.callback( err: Error | null, content: string | Buffer, sourceMap?: SourceMap, meta?: any );
- Первый аргумент должен быть
Errorилиnull - Второй аргумент —
stringилиBuffer. - Необязательно: третий аргумент — карта исходных данных, парсируемая модулем данного модуля.
- Необязательно: четвёртый параметр, игнорируемый webpack, может быть любым (например, некоторыми метаданными).
В случае вызова этой функции, вы должны вернуть undefined, чтобы избежать неоднозначных результатов загрузчика.
this.clearDependencies
clearDependencies();
Удалить все зависимости результата загрузчика, включая начальные и те, что принадлежат другим загрузчикам. Рассмотрите использование pitch.
this.context
Директория модуля. Может использоваться в качестве контекста для разрешения других вещей.
В примере: /abc, потому что resource.js находится в этой директории
this.data
Объект данных, используемый для обмена между фазой предварительной обработки и обычной фазой.
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.emitFile
emitFile(name: string, content: Buffer|string, sourceMap: {...}) Вывести файл. Это специфично для webpack.
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
this.environment
Проверить какие типы ES-функциональности можно использовать в сгенерированном коде.
Например,
{
// The environment supports arrow functions ('() => { ... }').
"arrowFunction": true,
// The environment supports BigInt as literal (123n).
"bigIntLiteral": false,
// The environment supports const and let for variable declarations.
"const": true,
// The environment supports destructuring ('{ a, b } = obj').
"destructuring": true,
// The environment supports an async import() function to import EcmaScript modules.
"dynamicImport": false,
// The environment supports an async import() when creating a worker, only for web targets at the moment.
"dynamicImportInWorker": false,
// The environment supports 'for of' iteration ('for (const x of array) { ... }').
"forOf": true,
// The environment supports 'globalThis'.
"globalThis": true,
// The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...').
"module": false,
// The environment supports optional chaining ('obj?.a' or 'obj?.()').
"optionalChaining": true,
// The environment supports template literals.
"templateLiteral": true
} this.fs
Доступ к свойству compilation's inputFileSystem.
this.getOptions(schema)
Извлечь заданные параметры загрузчика. Необязательно, принимает JSON-схему в качестве аргумента.
this.getResolve
getResolve(options: ResolveOptions): resolve resolve(context: string, request: string, callback: function(err, result: string)) resolve(context: string, request: string): Promise<string>
Создаёт функцию разрешения, аналогичную this.resolve.
Любые параметры в resolve опциях webpack возможны. Они объединяются с настроенными resolve опциями. Обратите внимание, что "..." может использоваться в массивах для расширения значения из resolve опций, например, { extensions: [".sass", "..."] }.
options.dependencyType — это дополнительная опция. Она позволяет указать тип зависимости, используемый для разрешения byDependency из resolve опций.
Все зависимости операции разрешения автоматически добавляются в зависимости текущего модуля.
this.hot
Информация о HMR для загрузчиков.
module.exports = function (source) {
console.log(this.hot); // true if HMR is enabled via --hot flag or webpack configuration
return source;
}; this.hashDigest
string
Кодировка для генерации хэша. См. output.hashDigest.
this.hashDigestLength
number
Длина префикса хэша для использования. См. output.hashDigestLength.
this.hashFunction
string function
Алгоритм хеширования. См. output.hashFunction.
this.hashSalt
string
Необязательная соль для обновления хэша через Node.JS' hash.update. См. output.hashSalt.
this.importModule
5.32.0+this.importModule(request, options, [callback]): Promise
Альтернативное лёгкое решение для дочернего компилятора для компиляции и выполнения запроса во время сборки.
-
request: строка запроса для загрузки модуля -
options:-
layer: укажите слой, в котором этот модуль размещён/скомпилирован -
publicPath: общедоступный путь, используемый для скомпилированных модулей
-
-
callback: необязательный обратный вызов в стиле Node.js, возвращающий экспорт модуля или объект пространства имён для ESM.importModuleвернёт Promise, если обратный вызов не предоставлен.
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /stylesheet\.js$/i,
use: ['./a-pitching-loader.js'],
type: 'asset/source', // we set type to 'asset/source' as the loader will return a string
},
],
},
}; a-pitching-loader.js
exports.pitch = async function (remaining) {
const result = await this.importModule(
this.resourcePath + '.webpack[javascript/auto]' + '!=!' + remaining
);
return result.default || result;
}; src/stylesheet.js
import { green, red } from './colors.js';
export default `body { background: ${red}; color: ${green}; }`; src/colors.js
export const red = '#f00'; export const green = '#0f0';
src/index.js
import stylesheet from './stylesheet.js';
// stylesheet will be a string `body { background: #f00; color: #0f0; }` at build time Вы, возможно, заметите кое-что в приведённом выше примере:
- У нас есть загрузчик-помощник,
- Мы используем синтаксис
!=!в этом загрузчике-помощнике, чтобы установить matchResource для запроса, т.е. мы будем использоватьthis.resourcePath + '.webpack[javascript/auto]'для соответствия сmodule.rules, а не с исходным ресурсом, -
.webpack[javascript/auto]— псевдорасширение шаблона.webpack[type], мы используем его для указания типа модуля по умолчанию, когда не указан другой тип модуля. Обычно он используется совместно с синтаксисом!=!.
Обратите внимание, что вышеприведённый пример упрощённый. Вы можете ознакомиться с полным примером на репозитории webpack.
this.loaderIndex
Индекс в массиве загрузчиков текущего загрузчика.
В примере: в loader1: 0, в loader2: 1
this.loadModule
loadModule(request: string, callback: function(err, source, sourceMap, module))
Разрешает указанный запрос до модуля, применяет все настроенные загрузчики и возвращает сгенерированный исходный код, sourceMap и экземпляр модуля (обычно экземпляр NormalModule). Используйте эту функцию, если вам нужно узнать исходный код другого модуля для генерации результата.
this.loadModule в контексте загрузчика по умолчанию использует правила разрешения CommonJS. Используйте this.getResolve с соответствующим dependencyType, например, 'esm', 'commonjs' или настроенным, прежде чем использовать другую семантику.
this.loaders
Массив всех загрузчиков. Он изменяется на фазе pitch.
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.mode
Считывает, в каком mode режиме работает webpack.
Возможные значения: 'production', 'development', 'none'
this.query
- Если загрузчик был настроен с объектом
options, он будет указывать на этот объект. - Если у загрузчика нет
options, но он был вызван со строкой запроса, это будет строка, начинающаяся с?.
this.request
Разрешённая строка запроса.
В примере: '/abc/loader1.js?xyz!/abc/node_modules/loader2/index.js!/abc/resource.js?rrr'
this.resolve
resolve(context: string, request: string, callback: function(err, result: string))
Разрешает запрос, подобно выражению require.
-
contextдолжен быть абсолютным путём к директории. Эта директория используется в качестве начальной точки для разрешения. -
request— запрос для разрешения. Обычно используются относительные запросы, такие как./relative, или запросы к модулям, такие какmodule/path, но также возможны абсолютные пути, такие как/some/path, в качестве запросов. -
callback— обычная функция обратного вызова в стиле Node.js, возвращающая разрешённый путь.
Все зависимости операции разрешения автоматически добавляются в зависимости текущего модуля.
this.resource
Часть запроса, относящаяся к ресурсу, включая запрос.
В примере: '/abc/resource.js?rrr'
this.resourcePath
Файл ресурса.
В примере: '/abc/resource.js'
this.resourceQuery
Запрос к ресурсу.
В примере: '?rrr'
this.rootContext
Начиная с webpack 4, ранее this.options.context предоставляется как this.rootContext.
this.sourceMap
Указывает, нужно ли генерировать source map. Поскольку генерация source map может быть ресурсоёмкой задачей, необходимо проверить, действительно ли source map запрошен.
this.target
Целевая среда компиляции. Передаётся из конфигурационных параметров.
Примеры значений: 'web', 'node'
this.utils
5.27.0+Доступ к следующим утилитам.
-
absolutify: Возвращает новую строку запроса, используя абсолютные пути, где это возможно. -
contextify: Возвращает новую строку запроса, избегая абсолютных путей, где это возможно. -
createHash: Возвращает новый объект Hash из предоставленной функции хеширования.
my-sync-loader.js
module.exports = function (content) {
this.utils.contextify(
this.context,
this.utils.absolutify(this.context, './index.js')
);
this.utils.absolutify(this.context, this.resourcePath);
const mainHash = this.utils.createHash(
this._compilation.outputOptions.hashFunction
);
mainHash.update(content);
mainHash.digest('hex');
// …
return content;
}; this.version
Версия API загрузчика. В настоящее время 2. Это полезно для обеспечения обратной совместимости. С помощью версии можно указать специальную логику или fallback для изменений, нарушающих обратную совместимость.
this.webpack
Этот флаг устанавливается в true, когда он скомпилирован webpack.
Свойства, специфичные для Webpack
Интерфейс загрузчика предоставляет всю информацию, связанную с модулем. Однако в редких случаях вам может потребоваться доступ к API самого компилятора.
Поэтому следует использовать их только в крайнем случае. Их использование снизит переносимость вашего загрузчика.
this._compilation
Доступ к текущему объекту Compilation webpack.
this._compiler
Доступ к текущему объекту Compiler webpack.
Устаревшие свойства контекста
this.debug
Флаг булевого типа. Он устанавливается при включении режима отладки.
this.inputValue
Передаётся от последнего загрузчика. Если вы хотели бы выполнить входной аргумент как модуль, рассмотрите чтение этой переменной для сокращения (для производительности).
this.minimize
Указывает, нужно ли минимизировать результат.
this.value
Передаёт значения следующему загрузчику. Если вы знаете, что экспортирует ваш результат, если он выполняется как модуль, установите это значение здесь (как единственный элемент массива).
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
Встроенное соответствие matchResource
В webpack v4 был введён новый синтаксис встраиваемых запросов. Представление <match-resource>!=! перед запросом установит 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 getStylesLoader = require.resolve('./getStyles');
module.exports = function (source) {
if (STYLES_REGEXP.test(source)) {
source = source.replace(STYLES_REGEXP, '');
return `import ${JSON.stringify(
this.utils.contextify(
this.context || this.rootContext,
`${this.resource}.css!=!${getStylesLoader}!${this.remainingRequest}`
)
)};${source}`;
}
return source;
}; extract-style-loader/getStyles.js
module.exports = function (source) {
const match = source.match(STYLES_REGEXP);
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://webpack.js.org/api/loaders