本文示例源代碼請戳github博客,建議你們動手敲敲代碼。html
本文不會介紹loader的一些使用方法,不熟悉的同窗請自行查看 Webpack loader
首先咱們來看一下爲何須要loader
,以及他能幹什麼?webpack
只能理解 JavaScript
和 JSON
文件。loader
讓 webpack
可以去處理其餘類型的文件,並將它們轉換爲有效模塊,以供應用程序使用,以及被添加到依賴圖中。node
本質上來講,loader
就是一個 node
模塊,這很符合 webpack
中「萬物皆模塊」的思路。既然是 node
模塊,那就必定會導出點什麼。在 webpack
的定義中,loader
導出一個函數,loader
會在轉換源模塊resource
的時候調用該函數。在這個函數內部,咱們能夠經過傳入 this
上下文給 Loader API 來使用它們。最終裝換成能夠直接引用的模塊。webpack
前面咱們已經知道,因爲 Webpack 是運行在 Node.js 之上的,一個 Loader 其實就是一個 Node.js 模塊,這個模塊須要導出一個函數。 這個導出的函數的工做就是得到處理前的原內容,對原內容執行處理後,返回處理後的內容。
一個簡單的loader源碼以下git
module.exports = function(source) { // source 爲 compiler 傳遞給 Loader 的一個文件的原內容 // 該函數須要返回處理後的內容,這裏簡單起見,直接把原內容返回了,至關於該 Loader 沒有作任何轉換 return source; };
因爲 Loader 運行在 Node.js 中,你能夠調用任何 Node.js 自帶的 API,或者安裝第三方模塊進行調用:github
const xml2js = require('xml2js'); const parser = new xml2js.Parser(); module.exports = function(source) { this.cacheable && this.cacheable(); const self = this; parser.parseString(source, function (err, result) { self.callback(err, !err && "module.exports = " + JSON.stringify(result)); }); };
這裏咱們事簡單實現一個xml-loader
;web
注意:若是是處理順序排在最後一個的loader
,那麼它的返回值將最終交給webpack
的require
,換句話說,它必定是一段可執行的JS
腳本 (用字符串來存儲),更準確來講,是一個node
模塊的JS
腳本,因此咱們須要用module.exports =
導出。
整個過程至關於這個 loader 把源文件npm
// 這裏是 source 模塊
轉化爲json
// example.js module.exports = '這裏是 source 模塊';
而後交給 require 調用方:segmentfault
// applySomeModule.js var source = require('example.js'); console.log(source); // 這裏是 source 模塊
寫完後咱們要怎麼在本地驗證呢?下面咱們來寫個簡單的demo進行驗證。api
首先咱們建立一個根目錄xml-loader,此目錄下 npm init -y生成默認的package.json文件 ,在文件中配置打包命令
"scripts": { "dev": "webpack-dev-server" },
以後npm i -D webpack webpack-cli
,安裝完webpack
,在根目錄 建立配置文件webpack.config.js
const path = require('path'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', }, module: { rules: [ { test: /\.xml$/, use: ['xml-loader'], } ] }, resolveLoader: { modules: [path.join(__dirname, '/src/loader')] }, devServer: { contentBase: './dist', overlay: { warnings: true, errors: true }, open: true } }
在根目錄建立一個src目錄,裏面建立index.js,
import data from './foo.xml'; function component() { var element = document.createElement('div'); element.innerHTML = data.note.body; element.classList.add('header'); console.log(data); return element; } document.body.appendChild(component());
同時還有一個foo.xml
文件
<?xml version="1.0" encoding="UTF-8"?> <note> <to>Mary</to> <from>John</from> <heading>Reminder dd</heading> <body>Call Cindy on Tuesday dd</body> </note>
最後把上面的xml-loader
放到src/loader
文件夾下。
完整的demo源碼請看
最終咱們的運行效果以下圖
至此一個簡單的webpack loader
就實現完成了。固然最終使用你能夠發佈到npm上。
當咱們配置loader時咱們常常會看到有這樣的配置
ules: [{ test: /\.html$/, use: [ { loader: 'html-loader', options: { minimize: true } }], }]
那麼咱們在loader中怎麼獲取這寫配置信息呢?答案是loader-utils
。這個由webpack提供的工具。下面咱們來看下使用方法
const loaderUtils = require('loader-utils'); module.exports = function(source) { // 獲取到用戶給當前 Loader 傳入的 options const options = loaderUtils.getOptions(this); return source; };
沒錯就是這麼簡單。
一、path.resolve
能夠簡單經過在 rule 對象設置 path.resolve 指向這個本地文件
{ test: /\.js$/ use: [ { loader: path.resolve('path/to/loader.js'), options: {/* ... */} } ] }
二、ResolveLoader
這個就是上面我用到的方法。ResolveLoader
用於配置 Webpack
如何尋找 Loader
。 默認狀況下只會去 node_modules 目錄下尋找,爲了讓 Webpack
加載放在本地項目中的 Loader
須要修改 resolveLoader.modules
。
假如本地的 Loader
在項目目錄中的 ./loaders/loader-name
中,則須要以下配置:
module.exports = { resolveLoader:{ // 去哪些目錄下尋找 Loader,有前後順序之分 modules: ['node_modules','./loaders/'], } }
加上以上配置後, Webpack
會先去 node_modules
項目下尋找 Loader
,若是找不到,會再去 ./loaders/
目錄下尋找。
三、npm linknpm link
專門用於開發和調試本地 npm
模塊,能作到在不發佈模塊的狀況下,把本地的一個正在開發的模塊的源碼連接到項目的 node_modules
目錄下,讓項目能夠直接使用本地的 npm
模塊。 因爲是經過軟連接的方式實現的,編輯了本地的 Npm 模塊代碼,在項目中也能使用到編輯後的代碼。
完成 npm link
的步驟以下:
package.json
已經正確配置好;npm
模塊根目錄下執行 npm link
,把本地模塊註冊到全局;npm link loader-name
,把第2步註冊到全局的本地 Npm 模塊連接到項目的 node_moduels
下,其中的 loader-name
是指在第1步中的package.json
文件中配置的模塊名稱。連接好 Loader
到項目後你就能夠像使用一個真正的 Npm 模塊同樣使用本地的 Loader
了。(npm link不是很熟,複製被人的)
在有些狀況下,有些轉換操做須要大量計算很是耗時,若是每次構建都從新執行重複的轉換操做,構建將會變得很是緩慢。 爲此,Webpack 會默認緩存全部 Loader 的處理結果,也就是說在須要被處理的文件或者其依賴的文件沒有發生變化時, 是不會從新調用對應的 Loader 去執行轉換操做的。
若是你想讓 Webpack 不緩存該 Loader 的處理結果,能夠這樣:
module.exports = function(source) { // 關閉該 Loader 的緩存功能 this.cacheable(false); return source; };
在默認的狀況下,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;
以上代碼中最關鍵的代碼是最後一行 module.exports.raw = true;
,沒有該行 Loader 只能拿到字符串。
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); }); };
參考