loader是一個函數,有一個參數是源文件內容,有內部的this,最後的loader必須有返回值不然會報錯:Final loader didn't return a Buffer or Stringcss
緣由:node
若是是處理順序排在最後一個的 Loader,那麼它的返回值將最終交給webpack的require,換句話說,它必定是一段用字符串來存儲可執行的JavaScript腳本webpack
{
test: /\.js$/
use: [
{
loader: path.resolve(__dirname, 'src', 'loaders', 'first-loader.js'),
options: {/*這裏是給loader傳入的參數會有一個專門的庫來處理這個參數,待會下面會講到*/}
}
]
}
複製代碼
若是把咱們本身寫的loader當作一個模塊來加載的時候,webpack默認的會去查找node_modules下面的模塊,可是咱們本身寫的loader沒有在node_modules中 這時候咱們怎麼樣讓他也查找咱們本身寫的loader呢?這時候咱們就須要設置一個屬性 resolveLoader他是一個對象,裏面有一個modules屬性 該屬性的值是一個數組 數組中 的每一個值就是要查找的loader存放地web
resolveLoader: {
modules: [
path.resolve('node_modules'),
path.resolve(__dirname, 'src', 'loaders')
]
},
複製代碼
再配置loaderjson
{
test: /\.less$/,
use: ['style-loader','less-loader']
}
複製代碼
單一職責,一個loader只作一件事情,這樣設計的緣由是由於,職責越單一,組合性就強,可配置性就好。數組
從右到左,鏈式執行,上一個loader的處理結果傳給下一個loader接着處理緩存
模塊化,一個loader就是一個模塊,單獨存在不依賴其餘的任何模塊 無狀態,就相似於純函數。bash
loader有實用工具loader-utils解析loader參數的和schema-utils校驗格式的網絡
loader的依賴app
一、緩存 webpack充分地利用緩存來提升編譯效率
this.cacheable();
複製代碼
二、異步 當一個 Loader 無依賴,可異步的時候我想都應該讓它再也不阻塞地去異步
// 讓 Loader 緩存
module.exports = function(source) {
var callback = this.async();
// 作異步的事
doSomeAsyncOperation(content, function(err, result) {
if(err) return callback(err);
callback(null, result);
});
};
複製代碼
三、傳入的源文件以buffer的形式
默認的狀況源文件是以 UTF-8 字符串的形式傳入給 Loader,設置module.exports.raw = true可以使用 buffer 的形式進行處理
module.exports.raw = true
複製代碼
四、獲取loader的options
const loaderUtils = require('loader-utils');
module.exports = function (source) {
// 獲取當前用戶給當前loader傳入的參數對象options
const options = loaderUtils.getOptions(this);
return source;
}
複製代碼
五、校驗傳入的options的值是否是符合要求
const loaderUtils = require('loader-utils');
const validate = require('schema-utils');
let json = {
"type": "object",
"properties": {
"content": {
"type": "string",
}
}
}
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
// 第一個參數是校驗的json 第二個參數是loader傳入的options 第三個參數是當前loader的名稱
validate(json, options, 'first-loader');
console.log(options.content)
}
// 當前規定 傳入的options必須是一個對象 他裏面的content的值必須是一個字符串 若是不知足要求則會有好的報錯 xxx應該是一個什麼類型
複製代碼
六、loader的返回其餘結果
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
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: {...})。 完整API
// 建立一個style標籤裏面放上源文件的樣式字符串 插到頁面上
module.exports = function (source) {
let script = (`
let style = document.createElement("style");
style.innerText = ${JSON.stringify(source)};
document.head.appendChild(style);
`);
return script;
}
複製代碼
let less = require('less');
module.exports = function (source) {
this.cacheable();
less.render(source, (err,result)=>{
console.log(result.css);
this.callback(err,result.css)
})
}
複製代碼
不要厭煩熟悉的事物,天天都進步一點;不要畏懼陌生的事物,天天都學習一點;