載入器介面

載入器是一個匯出函式的 JavaScript 模組。載入器執行器 呼叫此函式,並將前一個載入器或資源檔案的結果傳遞給它。函式的 this 內容由 webpack 和 載入器執行器 填入一些有用的方法,讓載入器(在其他事物中)可以將其呼叫樣式變更為非同步,或取得查詢參數。

第一個載入器傳遞一個引數:資源檔案的內容。編譯器預期最後一個載入器的結果。結果應該是表示模組 JavaScript 原始碼的 StringBuffer(轉換為字串)。也可以傳遞一個選用的 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
}

範例

以下各節提供不同類型的載入器的一些基本範例。請注意,mapmeta 參數是選用的,請參閱以下 this.callback

同步載入器

可以使用 returnthis.callback 同步傳回轉換後的 content

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。每個載入器都可以將其結果傳遞為 StringBuffer。編譯器會在載入器之間轉換它們。

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

那麼,為什麼載入器可能會利用「投擲」階段?

首先,傳遞給 pitch 方法的 data 也會在執行階段以 this.data 顯示,且可用於擷取和分享循環中較早階段的資訊。

module.exports = function (content) {
  return someSyncOperation(content, this.data.value);
};

module.exports.pitch = function (remainingRequest, precedingRequest, data) {
  data.value = 42;
};

其次,如果載入器在 pitch 方法中傳遞結果,則處理程序會轉身並略過其餘載入器。在上述範例中,如果 b-loaderpitch 方法回傳某個項目

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)

將目錄新增為 loader 結果的依賴項。

this.addDependency

addDependency(file: string)
dependency(file: string) // shortcut

將現有檔案新增為 loader 結果的依賴項,以便讓它們可被監控。例如,sass-loaderless-loader 會使用此功能,以便在任何匯入的 css 檔案變更時重新編譯。

this.addMissingDependency

addMissingDependency(file: string)

將非現有檔案新增為 loader 結果的依賴項,以便讓它們可被監控。與 addDependency 類似,但會在編譯期間處理檔案的建立,然後再正確附加監控程式。

this.async

告知 loader-runner,loader 打算非同步呼叫回傳。傳回 this.callback

this.cacheable

設定快取標記的函式

cacheable(flag = true: boolean)

預設情況下,loader 結果會標記為可快取。呼叫此方法並傳遞 false,即可讓 loader 的結果不可快取。

可快取的 loader 在輸入和依賴項未變更時,必須有確定性的結果。這表示 loader 不應有 this.addDependency 指定以外的依賴項。

this.callback

可同步或非同步呼叫的函式,用於傳回多個結果。預期的引數為

this.callback(
  err: Error | null,
  content: string | Buffer,
  sourceMap?: SourceMap,
  meta?: any
);
  1. 第一個引數必須是 Errornull
  2. 第二個引數是 stringBuffer
  3. 選用:第三個引數必須是 此模組 可解析的原始碼對應表。
  4. 選用:Webpack 忽略的第四個選項,可以是任何東西(例如一些元資料)。

如果呼叫此函數,您應傳回未定義以避免載入器結果不明確。

this.clearDependencies

clearDependencies();

移除載入器結果的所有依賴項,甚至包括初始依賴項和其他載入器的依賴項。考慮使用 pitch

this.context

模組的目錄。可用作解析其他項目的背景。

範例 中:/abc,因為 resource.js 在此目錄中

this.data

在 pitch 和一般階段之間共用的資料物件。

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

存取 compilationinputFileSystem 屬性。

this.getOptions(schema)

萃取指定的載入器選項。選擇性地接受 JSON schema 作為引數。

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 的解析函數。

webpack resolve 選項 下的任何選項都是可能的。它們與已設定的 resolve 選項合併。請注意,"..." 可用於陣列中,以延伸 resolve 選項中的值,例如 { extensions: [".sass", "..."] }

options.dependencyType 是額外的選項。它允許我們指定相依性的類型,用於從 resolve 選項中解析 byDependency

解析作業的所有相依性會自動新增為目前模組的相依性。

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.importModule

5.32.0+

this.importModule(request, options, [callback]): Promise

一個替代的輕量級解決方案,供子編譯器在建置時間編譯並執行請求。

  • request:用於載入模組的請求字串
  • 選項:
    • 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

您可能會注意到上述範例中的某些內容

  1. 我們有一個 pitching loader
  2. 我們在該 pitching loader 中使用 !=! 語法來設定請求的 matchResource,亦即我們將使用 this.resourcePath + '.webpack[javascript/auto]'module.rules 進行比對,而不是原始資源,
  3. .webpack[javascript/auto].webpack[type] 模式的偽延伸,我們使用它來指定在未指定其他模組類型時預設的 模組類型。它通常與 !=! 語法一起使用。

請注意,上述範例是一個簡化的範例,您可以查看 webpack 儲存庫中的完整範例

this.loaderIndex

目前 loader 在 loaders 陣列中的索引。

範例 中:在 loader1 中:0,在 loader2 中:1

this.loadModule

loadModule(request: string, callback: function(err, source, sourceMap, module))

將給定的請求解析為模組,套用所有已設定的載入器,並使用已產生的來源、來源地圖和模組實例 (通常是 NormalModule 的實例) 回呼。如果您需要知道其他模組的原始碼才能產生結果,請使用此函數。

在載入器內容中,this.loadModule 預設使用 CommonJS 解析規則。在使用不同的語意之前,請使用適當的 dependencyType (例如 'esm''commonjs' 或自訂的) 搭配 this.getResolve

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

讀取 webpack 執行的 mode

可能的值:'production''development''none'

this.query

  1. 如果載入器已使用 options 物件設定,這將指向該物件。
  2. 如果載入器沒有 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

說明是否應產生原始碼對應。由於產生原始碼對應可能是一項昂貴的任務,因此您應該檢查是否實際要求原始碼對應。

this.target

編譯目標。從組態選項傳遞。

範例值:'web''node'

this.utils

5.27.0+

存取 contextifyabsolutify 工具程式。

  • contextify:回傳一個新的請求字串,在可能的情況下避免絕對路徑。
  • absolutify:回傳一個新的請求字串,在可能的情況下使用絕對路徑。

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);
  // …
  return content;
};

this.version

Loader API 版本。目前為 2。這有助於提供向下相容性。使用版本,您可以指定自訂邏輯或因中斷變更而產生的替代方案。

this.webpack

當 webpack 編譯時,此布林值設為 true。

Webpack 特定屬性

Loader 介面提供所有模組相關資訊。不過在少數情況下,您可能需要存取編譯器 API 本身。

因此,您應僅將它們作為最後的手段。使用它們會降低 loader 的可攜性。

this._compilation

存取 webpack 的目前 Compilation 物件。

this._compiler

存取 webpack 的目前 Compiler 物件。

已棄用的內容屬性

this.debug

布林旗標。在偵錯模式中設定。

this.inputValue

從最後一個載入器傳遞。如果您要執行輸入參數作為模組,請考慮讀取此變數以取得捷徑(以提升效能)。

this.minimize

指示是否應將結果最小化。

this.value

傳遞值至下一個載入器。如果您知道如果將結果執行為模組時會匯出什麼,請在此處設定此值(作為唯一的元素陣列)。

this._module

駭入存取正在載入的模組物件。

錯誤回報

您可以透過以下方式從載入器內部回報錯誤

  • 使用 this.emitError。將回報錯誤,而不會中斷模組編譯。
  • 使用 throw(或其他未捕捉的例外狀況)。在載入器執行期間擲回錯誤,將導致目前的模組編譯失敗。
  • 使用 callback(在非同步模式中)。傳遞錯誤至 callback 也會導致模組編譯失敗。

例如

./src/index.js

require('./loader!./lib');

從載入器擲回錯誤

./src/loader.js

module.exports = function (source) {
  throw new Error('This is a Fatal Error!');
};

或在非同步模式中傳遞錯誤至 callback

./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.issuersplitChunks 中的 test

範例

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 版本開始提供。當在 stats 設定 中啟用 logging,和/或當 基礎架構記錄 啟用時,載入器可以記錄訊息,這些訊息將以各自的記錄器格式列印出來(stats、基礎架構)。

  • 載入器應優先使用 this.getLogger() 來記錄,這是 compilation.getLogger() 的捷徑,包含載入器路徑和已處理檔案。這類型的記錄會儲存在 Stats 中並適當地格式化。它可以由 webpack 使用者過濾和匯出。
  • 載入器可以使用 this.getLogger('name') 來取得一個具有子名稱的獨立記錄器。載入器路徑和已處理檔案仍會加入。
  • 載入器可以使用特定的後備邏輯來偵測記錄支援 this.getLogger ? this.getLogger() : console,以在使用不支援 getLogger 方法的舊版 webpack 時提供後備。

12 貢獻者

TheLarkInnjhnnstbroadleybyzyksokraEugeneHlushkojantimonsuperburritowizardofhogwartssnitin315chenxsanjamesgeorge007