Spec-Zone.ru › webpack 4

Интерфейс загрузчика

Загрузчик — это просто 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

  1. Если загрузчик был настроен с объектом options, он будет указывать на этот объект.
  2. Если у загрузчика нет options, но он был вызван со строкой запроса, то это будет строка, начинающаяся с ?.

Используйте метод getOptions из loader-utils для извлечения заданных опций загрузчика.

this.callback

Функция, которая может быть вызвана синхронно или асинхронно для возвращения нескольких результатов. Ожидаемые аргументы:

this.callback(
  err: Error | null,
  content: string | Buffer,
  sourceMap?: SourceMap,
  meta?: any
);
  1. Первый аргумент должен быть Error или null
  2. Второй аргумент — это string или Buffer.
  3. Необязательно: третий аргумент должен быть картой сопоставления, которая может быть обработана этим модулем .
  4. Необязательно: четвёртый параметр, игнорируемый 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API