天天看點

loader運作的總體流程學習

loader運作的總體流程學習

loader配置

loader

是導出為一個函數的

node

子產品。該函數在

loader

轉換資源的時候調用。給定的函數将調用

loader API

,并通過

this

上下文通路。

比對(test)單個 loader

比對(test)單個 loader,你可以簡單通過在 rule 對象設定 path.resolve 指向這個本地檔案

{
  test: /\.js$/
  use: [
    {
      loader: path.resolve('path/to/loader.js'),
      options: {/* ... */}
    }
  ]
}
           

比對(test)多個 loaders

你可以使用 resolveLoader.modules 配置,webpack 将會從這些目錄中搜尋這些 loaders。

resolveLoader: {
   modules: [path.resolve('node_modules'), path.resolve(__dirname, 'src', 'loaders')]
},
           

npm link

  • 確定正在開發的本地 Npm 子產品(也就是正在開發的 Loader)的 package.json 已經正确配置好; 在本地 Npm 子產品根目錄下執行 npm link,把本地子產品注冊到全局;
  • 在項目根目錄下執行 npm link loader-name,把第2步注冊到全局的本地 Npm 子產品連結到項目的 node_moduels 下,其中的 - loader-name 是指在第1步中的 package.json 檔案中配置的子產品名稱。
    npm link
               

alias

resolveLoader: {
        alias: {
            "babel-loader": resolve('./loaders/babel-loader.js'),
            "css-loader": resolve('./loaders/css-loader.js'),
            "style-loader": resolve('./loaders/style-loader.js'),
            "file-loader": resolve('./loaders/file-loader.js'),
            "url-loader": resolve('./loaders/url-loader.js')
        }
    },
           

loader用法

單個loader用法

  • 當一個 loader 在資源中使用,這個 loader 隻能傳入一個參數 - 這個參數是一個包含包含資源檔案内容的字元串
  • 同步 loader 可以簡單的傳回一個代表子產品轉化後的值。
  • 在更複雜的情況下,loader 也可以通過使用 this.callback(err, values…) 函數,傳回任意數量的值。錯誤要麼傳遞給這個 this.callback 函數,要麼扔進同步 loader 中。
  • loader隻能傳入一個包含包含資源檔案内容的字元串
  • 同步 loader 可以簡單的傳回一個代表子產品轉化後的值
  • loader 也可以通過使用 this.callback(err, values…) 函數,傳回任意數量的值
  • loader 會傳回一個或者兩個值。第一個值的類型是 JavaScript 代碼的字元串或者 buffer。第二個參數值是 SourceMap,它是個 JavaScript 對象

多個loader

當鍊式調用多個 loader 的時候,請記住它們會以相反的順序執行。取決于數組寫法格式,從右向左或者從下向上執行。

  • 最後的 loader 最早調用,将會傳入原始資源内容。
  • 第一個 loader 最後調用,期望值是傳出 JavaScript 和 source map(可選)。
  • 中間的 loader 執行時,會傳入前一個 loader 傳出的結果。

單個loader用法

  • 最後的 loader 最早調用,将會傳入原始資源内容。
  • 第一個 loader 最後調用,期望值是傳出 JavaScript 和 source map(可選)。
  • 中間的 loader 執行時,會傳入前一個 loader 傳出的結果。

用法準則

  • 簡單
loaders 應該隻做單一任務。這不僅使每個 loader 易維護,也可以在更多場景鍊式調用。
  • 鍊式(Chaining)
利用 loader 可以鍊式調用的優勢。寫五個簡單的 loader 實作五項任務,而不是一個 loader 實作五項任務
  • 子產品化(Modular)
保證輸出子產品化。loader 生成的子產品與普通子產品遵循相同的設計原則。
  • 無狀态(Stateless)
確定 loader 在不同子產品轉換之間不儲存狀态。每次運作都應該獨立于其他編譯子產品以及相同子產品之前的編譯結果。

loader 工具庫(Loader Utilities)

  • loader-utils 包。它提供了許多有用的工具,但最常用的一種工具是擷取傳遞給 loader 的選項
  • schema-utils 包配合 loader-utils,用于保證 loader 選項,進行與 JSON Schema 結構一緻的校驗
  • loader 依賴(Loader Dependencies)
如果一個 loader 使用外部資源(例如,從檔案系統讀取),必須聲明它。這些資訊用于使緩存 loaders 無效,以及在觀察模式(watch mode)下重編譯。
  • 子產品依賴(Module Dependencies)

根據子產品類型,可能會有不同的模式指定依賴關系。例如在 CSS 中,使用 @import 和 url(…) 語句來聲明依賴。這些依賴關系應該由子產品系統解析。

絕對路徑(Absolute Paths)

不要在子產品代碼中插入絕對路徑,因為當項目根路徑變化時,檔案絕對路徑也會變化。

loader-utils

中的

stringifyRequest

方法,可以将絕對路徑轉化為相對路徑。

同等依賴(Peer Dependencies)

  • 如果你的 loader 簡單包裹另外一個包,你應該把這個包作為一個 peerDependency 引入。
  • 這種方式允許應用程式開發者在必要情況下,在 package.json 中指定所需的确定版本。

API

緩存結果

webpack充分地利用緩存來提高編譯效率

異步

當一個 Loader 無依賴,可異步的時候我想都應該讓它不再阻塞地去異步

// 讓 Loader 緩存
module.exports = function(source) {
    var callback = this.async();
    // 做異步的事
    doSomeAsyncOperation(content, function(err, result) {
        if(err) return callback(err);
        callback(null, result);
    });
};
           

raw loader

預設的情況源檔案是以

UTF-8

字元串的形式傳入給 Loader,設定

module.exports.raw = true

可使用 buffer 的形式進行處理

獲得 Loader 的 options

const loaderUtils = require('loader-utils');
module.exports = function(source) {
  // 擷取到使用者給目前 Loader 傳入的 options
  const options = loaderUtils.getOptions(this);
  return source;
};
           

傳回其它結果

Loader有些場景下還需要傳回除了内容之外的東西。

module.exports = function(source) {
  // 通過 this.callback 告訴 Webpack 傳回的結果
  this.callback(null, source, sourceMaps);
  // 當你使用 this.callback 傳回内容時,該 Loader 必須傳回 undefined,
  // 以讓 Webpack 知道該 Loader 傳回的結果在 this.callback 中,而不是 return 中 
  return;
};
           

完整格式

this.callback(
    // 當無法轉換原内容時,給 Webpack 傳回一個 Error
    err: Error | null,
    // 原内容轉換後的内容
    content: string | Buffer,
    // 用于把轉換後的内容得出原内容的 Source Map,友善調試
    sourceMap?: SourceMap,
    // 如果本次轉換為原内容生成了 AST 文法樹,可以把這個 AST 傳回,
    // 以友善之後需要 AST 的 Loader 複用該 AST,以避免重複生成 AST,提升性能
    abstractSyntaxTree?: AST
);
           

同步與異步

Loader 有同步和異步之分,上面介紹的 Loader 都是同步的 Loader,因為它們的轉換流程都是同步的,轉換完成後再傳回結果。 但在有些場景下轉換的步驟隻能是異步完成的,例如你需要通過網絡請求才能得出結果,如果采用同步的方式網絡請求就會阻塞整個建構,導緻建構非常緩慢。

module.exports = function(source) {
    // 告訴 Webpack 本次轉換是異步的,Loader 會在 callback 中回調結果
    var callback = this.async();
    someAsyncOperation(source, function(err, result, sourceMaps, ast) {
        // 通過 callback 傳回異步執行後的結果
        callback(err, result, sourceMaps, ast);
    });
};
           

處理二進制資料

在預設的情況下,Webpack 傳給 Loader 的原内容都是 UTF-8 格式編碼的字元串。 但有些場景下 Loader 不是處理文本檔案,而是處理二進制檔案,例如 file-loader,就需要 Webpack 給 Loader 傳入二進制格式的資料。 為此,你需要這樣編寫 Loader:

module.exports = function(source) {
    // 在 exports.raw === true 時,Webpack 傳給 Loader 的 source 是 Buffer 類型的
    source instanceof Buffer === true;
    // Loader 傳回的類型也可以是 Buffer 類型的
    // 在 exports.raw !== true 時,Loader 也可以傳回 Buffer 類型的結果
    return source;
};
// 通過 exports.raw 屬性告訴 Webpack 該 Loader 是否需要二進制資料 
module.exports.raw = true;
           

緩存

在有些情況下,有些轉換操作需要大量計算非常耗時,如果每次建構都重新執行重複的轉換操作,建構将會變得非常緩慢。 為此,Webpack 會預設緩存所有 Loader 的處理結果,也就是說在需要被處理的檔案或者其依賴的檔案沒有發生變化時, 是不會重新調用對應的 Loader 去執行轉換操作的。

module.exports = function(source) {
  // 關閉該 Loader 的緩存功能
  this.cacheable(false);
  return source;
};
           

其它 Loader API

  • 完整API
方法名 含義

this.context

目前處理檔案的所在目錄,假如目前 Loader 處理的檔案是 /src/main.js,則 this.context 就等于 /src

this.resource

目前處理檔案的完整請求路徑,包括 querystring,例如 /src/main.js?name=1。

this.resourcePath

目前處理檔案的路徑,例如 /src/main.js

this.resourceQuery

目前處理檔案的 querystring

this.target

等于 Webpack 配置中的 Target

this.loadModule

但 Loader 在處理一個檔案時,如果依賴其它檔案的處理結果才能得出目前檔案的結果時,就可以通過 this.loadModule(request: string, callback: function(err, source, sourceMap, module)) 去獲得 request 對應檔案的處理結果

this.resolve

像 require 語句一樣獲得指定檔案的完整路徑,使用方法為 resolve(context: string, request: string, callback: function(err, result: string))

this.addDependency

給目前處理檔案添加其依賴的檔案,以便再其依賴的檔案發生變化時,會重新調用 Loader 處理該檔案。使用方法為 addDependency(file: string)

this.addContextDependency

和 addDependency 類似,但 addContextDependency 是把整個目錄加入到目前正在處理檔案的依賴中。使用方法為 addContextDependency(directory: string)

this.clearDependencies

清除目前正在處理檔案的所有依賴,使用方法為 clearDependencies()

this.emitFile

輸出一個檔案,使用方法為 emitFile(name: string, content: Buffer/string, sourceMap: {…})

loader-utils.stringifyRequest

Turns a request into a string that can be used inside require() or import while avoiding absolute paths. Use it instead of JSON.stringify(…) if you’re generating code inside a loader 把一個請求字元串轉成一個字元串,以便能在require或者import中使用以避免絕對路徑。如果你在一個loder中生成代碼的話請使用這個而不要用JSON.stringify()

loader-utils.interpolateName

Interpolates a filename template using multiple placeholders and/or a regular expression. The template and regular expression are set as query params called name and regExp on the current loader’s context. 使用多個占位符或一個正規表達式轉換一個檔案名的子產品。這個模闆和正規表達式被設定為查詢參數,在目前loader的上下文中被稱為name或者regExp

繼續閱讀