1. 程式人生 > >如何編寫一個webpack loader

如何編寫一個webpack loader

webpack

loader是什麼

webpack官方定義

A loader is just a JavaScript module that exports a function

  • 從語法角度看,loader就是一個普通的Node.js模組,只是必須以函式格式匯出來供使用。如果有必要可以使用一切Node.js功能模組。

  • 從功能角度看,一個loader是在應用中作用於指定格式的資原始檔並將其按照一定格式轉換輸出。例如:sass-loader將scss檔案轉換為標準css檔案輸出。

 

1

2

3

4

5

6

 

// base loader

var fs = require("fs");

module.exports = function(source) {

return source;

};

loader 的種類

  • preloaders 就是在呼叫loader之前需要呼叫的loader, 他不做任何程式碼的轉換,只是進行檢查
  • loaders 就是我們本文章重點講述的用來處理檔案,做程式碼轉換的loader,例如將 scss檔案轉換為css檔案。
  • postloaders 就是在呼叫loader之後需要呼叫的loader,比如進行程式碼覆蓋率測試

loader如何使用

  • loader 是支援鏈式執行的,如處理 sass 檔案的 loader,可以由 sass-loader、css-loader、style-loader 組成,由 compiler 編譯器對其由右向左執行,第一個 loader 將會拿到需處理的原內容,上一個 loader 處理後的結果回傳給下一個接著處理,最後的 Loader 將處理後的結果以 String 或 Buffer 的形式返回給 compiler。

  • 每個 loader 只做該做的事,純粹的事,而不希望一籮筐的功能都整合到一個 loader 中。

  • loader函式還可以選擇使用 JSON 格式來傳遞資料。

loader 呼叫語法

  • 命令列呼叫
 

1

 

webpack --module-bind jade --module-bind 'css=style!css'

  • require呼叫

在需要作用的資原始檔名前面加上loader名稱和 感嘆號。

 

1

 

require('style-loader!css-loader?modules!./styles.css');

  • webpack配置檔案

webpack 2.x語法中需要寫全loader全名,不再允許省略-loader

語法格式一:

webpack 1.x語法

 

1

2

3

4

5

6

7

8

 

{

module: {

loaders: [{

test: /\.scss$/,

loader: 'style!css!sass'

}]

}

};

webpack 2.x語法

 

1

2

3

4

5

6

7

8

 

{

module: {

loaders: [{

test: /\.scss$/,

loader: 'style-loader!css-loader!sass-loader'

}]

}

};

語法格式二:

webpack 1.x

 

1

2

3

4

5

6

7

8

 

{

module: {

loaders: [{

test: /\.scss$/,

loader: ['style','css','sass']

}]

}

};

語法格式三:

webpack 2.x 語法

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

 

{

module: {

rules: [{

test: /\.scss$/,

use: [

'style-loader',

'css-loader',

{

loader: "sass-loader",

options: {

/* ... */

}

}

]

}]

}

};

loader 執行順序

當一個檔案從檔案系統中被讀取進入到loader執行序列中,其執行次序如下:

  1. webpack配置檔案中 preloaders 配置項 (webpack2.x移除)
  2. webpack配置檔案中 loaders 配置項
  3. request請求中指定的loaders,例如:

     

    1

     

    require('raw!./file.js')

  4. webpack配置檔案中 postloaders 配置項(webpack2.x移除)

在某些情況下,我們需要在js模組請求中覆蓋webpack配置檔案中的loaders順序來實現一些特定需求。具體規則如下:

  • 模組請求引數最前面新增感嘆號 !來禁用配置檔案中的preLoaders
 

1

 

require("!raw!./script.coffee")

  • 模組請求引數最前面新增雙感嘆號 !!來禁用配置檔案中所有的loaders
 

1

 

require("!!raw!./script.coffee")

  • 模組請求引數最前面新增 -!來禁用配置檔案中preLoadersloaders,除了postLoaders
 

1

 

require("-!raw!./script.coffee")

官方的建議

  • 對於結果為js檔案的情況,建議在loaders中處理
  • 對於預先編譯的非js(如coffee等)轉換js檔案,建議在preloader中處理,如果不是全域性應用也可以在 loaders中處理
  • 對於相同的語言,建議preloader 和 postloader 過程中處理

如何編寫一個loader

loader 輸出處理結果

  • 同步模式(sync mode)

直接返回loader的處理結果。

 

1

2

3

 

module.exports = function(content) {

return someSyncOperation(content);

};

  • 非同步模式(async mode)

呼叫 this.async()來獲取this.callback()方法,然後在非同步呼叫的回撥函式中通過callback返回null以及處理結果

 

1

2

3

4

5

6

7

8

 

module.exports = function(content) {

var callback = this.async();

if(!callback) return someSyncOperation(content);

someAsyncOperation(content, function(err, result) {

if(err) return callback(err);

callback(null, result);

});

};

Raw loader

預設情況下,不同loader之間是以UTF-8格式的文字字串來傳遞資料。我們可以通過設定module.exports.raw = true; 來選擇使用Buffer資料格式來傳遞資料,而編譯器會負責相鄰loader之間的格式轉換。

 

1

2

3

4

5

6

7

 

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 Loader

多個loader的鏈式呼叫預設都是遵循從右到左的順序。但在某些情況下,loaders並不關心前一個loader的處理結果或者資原始檔資料,它僅僅關心元資料metadata即原始輸入內容。

pitch方法則支援從左到右的執行順序,當pitch方法執行完畢,loaders呼叫流程反轉並跳過剩餘未執行的loaders,繼續呼叫當前loader更左側的loaders。並且通過 data屬性來在當前loader的pitch方法呼叫和 normal方法呼叫之間來傳遞資料。

 

1

2

3

4

5

6

7

8

9

10

 

module.exports = function(content) {

return someSyncOperation(content, this.data.value);

};

module.exports.pitch = function(remainingRequest, precedingRequest, data) {

if(someCondition()) {

// fast exit

return "module.exports = require(" + JSON.stringify("-!" + remainingRequest) + ");";

}

data.value = 42;

};

如何做好loader開發

善用loader中的this

loader函式作用域內的 this 變數掛載了一些很有用的方法屬性來實現諸如非同步呼叫、query引數獲取等功能。

  • query則能獲取到 Loader 上附有的引數。 如 require("./somg-loader?ls"); 通過 query 就可以得到 “ls” 了。
  • emitFile 能夠讓開發者更方便的輸出一個 file 檔案,這是 webpack 特有的方法,使用的方法也很直接
 

1

 

emitFile(name: string, content: Buffer|String, sourceMap: {...})

啟用結果快取 cacheable

 

1

2

3

4

 

module.exports = function(source) {

this.cacheable();

return source;

};

呼叫exec 方法執行一些程式碼片段。

 

1

 

exec(code: string, filename: string)

loader-utils

loader開發中經常用到的一個工具模組,其中提供了一些很有用的解析方法。點選檢視文件

code split

require.ensure使得我們可在所有的dependencies項載入完畢後,再執行回撥

require.ensure僅僅是載入元件,並不會執行,若要執行,需要藉助傳進去的require引數

 

1

2

3

4

 

require.ensure(["module-a", "module-b"], function(require) {

var a = require("module-a");

// ...

});

參考資料

原文https://fengmiaosen.github.io/2017/01/07/write-webpack-loader/