在前面的內容中,咱們學習了Webpack的基本知識、經常使用腳手架和性能優化,雖說大部分的開發場景社區已經又成熟的模塊給咱們使用,可是遇到特殊狀況仍是須要本身有獨立開發的能力,所以今天咱們一塊兒來學習如何編寫自定義Loader。javascript
Webpack中loader是一個CommonJs風格的函數,接收輸入的源碼,經過同步或異步的方式替換源碼後進行輸出。java
module.exports = function(source, sourceMap, meta) { }
須要注意的是,該導出函數必須使用function,不能使用箭頭函數,由於loader編寫過程當中會常常使用到this
訪問選項和其餘方法。node
咱們先編寫一個基本的Loader,完成的工做很簡單,那就是把輸出的字符串進行替換。webpack
1.新建loader-example目錄,執行npm初始化,並安裝webpackweb
mkdir loader-example cd loadeer-example npm init -y npm install webpack webpack-cli
2.構建項目目錄npm
|----loader # loader目錄 |----replace-loader.js # 替換字符串的Loader |----src # 應用源碼 |----index.js # 首頁 |----package.json |----webpack.config.js
3.編寫loader/replace-loader.jsjson
module.exports = function(source) { return source.replace(/World/g, 'Loader'); };
本例中咱們Loader只是簡單的將源碼中的」World「替換成了」Loader「。性能優化
4.編寫src/index.jsbash
console.log('Hello World');
5.編寫webpack.config.jsbabel
const path = require('path'); module.exports = { entry: './src/index', target: 'node', // 咱們編譯爲Node.js環境下的JS,等下直接使用Node.js執行編譯完成的文件 output:{ path: path.resolve(__dirname, 'build'), filename: '[name].js' }, module:{ rules:[ { test:/\.js$/, use: 'replace-loader' } ] }, resolveLoader: { modules: ['./node_modules', './loader'] // 配置loader的查找目錄 } };
6.編寫package.json
{ "scripts":{ "build":"webpack" } }
7.執行構建
npm run build
8.構建完成後,執行build/main.js
node build/main.js
此時終端輸出以下,咱們編寫的Loader工做正常。
Hello Loader
咱們使用第三方loader時常常能夠看到傳遞選項的狀況:
{ test:/\.js$/, use:[ { loader:'babel-loader', options:{ plugins:['@babel/transform-runtime'], presets:['@babel/env'] } } ] }
在Loader編寫時,Webpack中官方推薦經過loader-utils來讀取配置選項,咱們須要先安裝。
npm install loader-utils
咱們給剛纔編寫的replace-loader傳遞一個選項,容許自定義替換結果。
const loaderUtils = require('loader-utils'); module.exports = function(source) { const options = loaderUtils.getOptions(this); return source.replace(/World/g, options.text); };
接下來編輯webpack.config.js,給replace-loader傳遞選項。
module.exports = { module:{ rules:[ { test:/\.js$/, use:[ { loader:'replace-loader', options:{ text: 'Webpack4' } } ] } ] }, resolveLoader:{ modules: ['./node_modules', './loader'] } };
執行構建以後用Node.js執行build/main.js,能夠看到輸出的內容已經發生變化了。
Hello Webpack4
在Loader中,若是存在異步調用,那麼就沒法直接經過return返回構建後的結果了,此時須要使用到Webpack提供的回調函數將數據進行回調。
Webpack4給Loader提供了this.async()
函數,調用以後返回一個callback,callback的簽名以下:
function callback( err: Error|null, content: string|Buffer, sourceMap?:SourceMap, meta?: any )
例如咱們須要在loader中調用setTimeout進行等待,則相應的代碼以下:
module.exports = function(source) { const callback = this.async(); setTimeout(() => { const output = source.replace(/World/g, 'Webpack4'); callback(null, output); }, 1000); }
執行構建,Webpack會等待一秒,而後再輸出構建內容,經過Node.js執行構建後的文件,輸出以下
Hello Webpack4
默認狀況下,資源文件會被轉化爲 UTF-8 字符串,而後傳給 loader。經過設置 raw
,loader 能夠接收原始的 Buffer
。好比處理非文本文件時(如圖片等等)。
module.exports = function(source) { assert(source instanceof Buffer); return someSyncOperation(source); }; module.exports.raw = true; // 設置當前Loader爲raw loader, webpack會將原始的Buffer對象傳入
babel-loader在使用時能夠加載.babelrc配置文件來配置plugins和presets,減小了webpack.config.js的代碼量,便於維護。接下來咱們編寫一個i18n-loader,經過讀取語言配置文件完成語言轉換。
|----loader |----i18n-loader.js # loader |----i18n |----zh.json # 中文語言包 |----src |----index.js # 入口文件 |----webpack.config.js
i18n/zh.json
{ "hello": "你好", "today": "今天" }
loader/i18n-loader.js
const loaderUtils = require('loader-utils'); const path = require('path'); module.exports = function (source) { const options = loaderUtils.getOptions(this); const locale = options ? options.locale : null; // 讀取語言配置文件 let json = null; if (locale) { const filename = path.resolve(__dirname, '..', 'i18n', `${locale}.json`); json = require(filename); } // 讀取語言標記 {{}} const matches = source.match(/\{\{\w+\}\}/g); for (const match of matches) { const name = match.match(/\{\{(\w+)\}\}/)[1].toLowerCase(); if (json !== null && json[name] !== undefined) { source = source.replace(match, json[name]); } else { source = source.replace(match, name); } } return source; }
src/index.js
console.log('{{Hello}}, {{Today}} is a good day.');
webpack.config.js
const path = require('path'); module.exports = { entry: './src/index', output: { path: path.resolve(__dirname, 'build'), filename: '[name].js' }, target: 'node', module: { rules: [ { test: /\.js$/, use: [ { loader: 'i18n-loader', options: { // 傳遞選項 locale: 'zh' } } ] } ] }, resolveLoader: { modules: ['./node_modules', './loader'] } };
package.json
{ "scripts":{ "build":"webpack" } }
npm run build
構建完畢後使用Node.js執行build/main.js輸出以下:
你好, 今天 is a good day.
能夠看到i18n-loader成功讀取了配置文件。
本文簡要介紹了Webpack中如何編寫一個自定義的loader,權當拋磚引玉,更多的用法等待讀者在實際工做中去挖掘,要想掌握Webpack的高級知識,Loader是必不可少的技能,有時候若是社區找不到合適的Loader,你們能夠根據須要本身進行開發。