關於Webpack中Loader與Plugin的實踐

最近看一下webpack相關的內容,談一下如何編寫loader和plugin

在這裏插入圖片描述

01 前言

相信大家對webpack也有一定的瞭解,其實深入淺出webpack這本書也看了很多遍,每一次看都會有一些細節之前沒有注意到,我覺得其實可以把它當成是一本工具書來看,之前我也是隻看配置都有點讓你看不過來,更別說其他的了。

所以今天我就說一下如何編寫一個loader與plugin,以及它們之間有什麼卻別等。

02 Loader

Loader其實就是一個轉換器,把你輸入的內容翻譯一遍,本質上是沒有什麼變化的,就像中文翻譯成英文一樣。我們其實在不知不覺當中也使用了很多的Loader,但是我們沒有過多關注而已。常用的Loader有以下幾類:

常見Loader

語言轉換類

  • babel-loader:把ES6轉成ES5
  • ts-loader:把TypeScript轉成JavaScript
  • sass-loader:把scss/sass轉成css
  • less-loader:把less代碼轉成css
  • css-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文件,盤它。

運行/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

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章