最近看一下webpack相關的內容,談一下如何編寫loader和plugin
01 前言
相信大家對webpack也有一定的瞭解,其實深入淺出webpack這本書也看了很多遍,每一次看都會有一些細節之前沒有注意到,我覺得其實可以把它當成是一本工具書來看,之前我也是隻看配置都有點讓你看不過來,更別說其他的了。
所以今天我就說一下如何編寫一個loader與plugin,以及它們之間有什麼卻別等。
02 Loader
Loader其實就是一個轉換器,把你輸入的內容翻譯一遍,本質上是沒有什麼變化的,就像中文翻譯成英文一樣。我們其實在不知不覺當中也使用了很多的Loader,但是我們沒有過多關注而已。常用的Loader有以下幾類:
常見Loader
語言轉換類
babel-loader
:把ES6轉成ES5ts-loader
:把TypeScript轉成JavaScriptsass-loader
:把scss/sass轉成cssless-loader
:把less代碼轉成csscss-loader
:加載css,文件導入等
文件加載類
raw-loader
:把文本文件加載到代碼中file-loader
:將文件輸出到一個文件夾中,使用相對路徑引用輸出文件source-map-loader
:加載額外的SourceMap文件,方便斷點調試node-loader
:加載Node.js原生模塊的.node文件json-loader
:加載json文件
其他loader
vue-loader
:加載.vue文件ui-component-loader
:按需加載組件庫i18n-loader
:加載多語言版本ignore-loader
:忽略部分文件
Loader配置
module.exports = {
module:{
rules:[
{
test:/\.scss/,
use:[
'style-loader',
{
'css-loader',
options:{
minimize:true
}
},
'sass-loader'
],
}
]
}
}
以上的代碼意思就是對.scss文件的轉換過程,test的配置就是對某一類文件進行轉換,use是使用的loader(轉換器),它是一個數組,遵循從右往左的使用。先sass-loader再css-loader再style-loader。
加載本地Loader
我們默認的loader都是從npm上面下載的,但是假如我們要使用自己本地寫的loader怎麼辦呢?我們在webpack裏面有這樣一個配置resolveLoader
,它的意思就是說我們使用哪裏的loader來加載文件,可以配置多個地方:
module.exports = {
resolveLoader:{
modules:['node_modules','yourPath']
}
}
意思就是說我們可以自定義loader的路徑,默認就是從node_modules裏面找,但是假如你的自定義loader放在本地,可以把你的路徑寫在yourPath裏面(相對路徑)。匹配規則就是從左向右查找。
Loader編寫
說了這麼多,我們自己來寫一個簡單的loader吧,雖然是一個沒有意義的loader,但是也讓大家有一個簡單的印象,原來寫一個loader是很簡單的。
- 新建一個目錄,如myLoader
- 進入目錄初始化package.json文件,安裝webpack
npm init -y
,npm i -D webpack webpack-cli
- 根目錄下新建文件夾src,然後創建入口文件index.js ——
src/index.js
- 根目錄下新建文件夾loaders,然後新建自定義myLoader.js——
loaders/myloader.js
- 根目錄下新建webpack配置文件
webpack.config.js
- 在package.json配置webpack打包build命令
package.json
{
"name": "myLoader",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11"
}
}
index.js
console.log('世界上最好的語言是PHP!')
myLoader.js
const loaderUtils = require('loader-utils');
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
const result = source.replace('PHP', options.name);
return result;
}
webpack.config.js
const path = require("path");
module.exports = {
mode: "development",
entry: {
main: "./src/index.js",
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: path.resolve(__dirname, "./loaders/myLoader.js"),
options: {
name: "JavaScript",//這裏就是你要替換的值
},
},
},
],
},
};
好了,到此爲止我們已經成功地寫了一個loader,接下來我們就測試一下是否真的work!運行npm run build
之後會輸出一個dist文件夾,裏面有一個main.js文件,盤它。
03 Plugin
相對於Loader來說,其實plugin的機制更加靈活,它可以在webpack的運行過程中改變輸出結果。簡單來說就是爲輸出添磚加瓦。
常見Plugin
extract-text-webpack-plugin
:把JS中的css代碼提到單獨文件中webpack-parallel-uglify-plugin
:多進程代碼壓縮html-webpack-plugin
:簡化HTML文件創建dll-plugin
:提高webpack構建速度ignore-plugin
:忽略部分文件
Plugin的工作原理
我們先來看一下簡單的插件是怎麼編寫出來的:
class myPlugin {
constructor(options){
}
apply(complier){
complier.plugin('compilation',function(compilation){
})
}
}
//導出
module.exports = myPlugin;
工作流程:
- webpack啓動,執行new myPlugin(options),初始化插件並獲取實例
- 初始化complier對象,調用myPlugin.apply(complier)給插件傳入complier對象
- 插件實例獲取complier,通過complier.plugin監聽webpack廣播的事件,通過complier對象操作webpack
Plugin編寫
俗話說,talk is cheap,show me the code
還是沿用上面的代碼結構,在根目錄下面建一個myPlugins文件夾,裏面建一個myPlugin.js文件,我們就自定義一個plugin:
class myPlugin {
constructor(doneCallback, failCallback) {
this.doneCallback = doneCallback;
this.failCallback = failCallback;
}
apply(compiler) {
compiler.hooks.done.tap('myPlugin', (stats) => {
this.doneCallback(stats);
});
compiler.hooks.failed.tap('myPlugin', (err) => {
this.failCallback(err);
});
}
}
module.exports = myPlugin;
然後我們在webpack.config.js
文件裏面先引入插件
,然後配置插件
即可。
還是先打包一下,你會發現在打包過程中,webpack會廣播默認的事件,這裏我就監聽了webpack的done事件
(webpack構建成功,即將退出)和fail事件
(webpack構建失敗,即將退出)。
const myPlugin = require("./plugins/myPlugin");
module.exports = {
plugins: [
new myPlugin(
() => {
//throw new Error('Error!')
console.log("成功監聽到結束事件,可以執行你想要的函數!");
},
(error) => {
console.log(error);
}
),
],
}
我們運行之後發現是可以監聽的,這裏我把兩種情況都試一下。首先正常構建,然後手動拋出錯誤,結果如下:
04 小結
今天沒事就折騰一下看似簡單的東西,比如想看一下一些經典loader和plugin的源代碼,自信的我還想看一下webpack的源代碼,發現看不懂,就此打住了。
不過其實plugin和loader的區別也是面試常問的,也會問你知道怎麼寫loader和plugin的問題,雖然自己實現的很簡單甚至有點幼稚,自己感覺還好。
相信大家讀了之後應該有自己的理解,以後遇到這個問題也會有自己的想法,大家實踐一下就知道其中的奧祕了。
參考文章:深入淺出Webpack