webpack實戰之手寫一個簡易的loader和plugin

這是我參與8月更文挑戰的第5天,活動詳情查看:8月更文挑戰html

🔔序言

對於 webpack 來講, loaderplugin 能夠算是需求程度最爲普遍的配置項了。可是呢,單單止步於配置可能還不夠。若是咱們本身有時候想要 diy 一個需求,可是 webpack 又沒有相關的 loaderplugin 。那這個時候咱們可能就得開始造點輪子來供給本身使用了。node

所以,在今天的文章當中,將帶領你們手寫一個簡易的 loaderplugin ,並學會如何在項目中運用本身所編寫的 loaderpluginwebpack

一塊兒來學習吧~📢web

🎵1、如何編寫一個Loader

1. 碎碎念

以前的文章中咱們講到了關於 loader 的一些配置。那若是把那些引用的 loader 改成咱們寫的 loader ,該怎麼處理呢?npm

如今,咱們來了解一下,如何手寫一個簡易的 loader ,並運用到咱們的項目當中。設計模式

2. 項目結構

首先用一張圖,來看咱們的項目結構以下圖所示:bash

1-loader項目結構.png

其中 loaders 文件夾下放置咱們想要寫的 loader ,同時裏面的 replaceLoader.js 文件放置咱們即將要寫的 loader 的代碼邏輯。以後,index.js 文件是咱們的入口文件,放置咱們的業務邏輯。 webpack.config.js 文件放置關於 webpack 的相關配置,而 dist 文件夾內的內容,放置的是咱們經過 webpack 打包後,生成的打包文件。markdown

3. 業務代碼編寫

(1)入口文件代碼

如今,咱們先來編寫入口文件 index.js 的代碼。具體代碼以下:app

console.log('hello monday');
複製代碼

(2)編寫loader

入口文件的內容很簡單,咱們想要達到的目的就是輸出 hello monday 這個語句。如今,咱們來編寫 loader 的內容,已達到對入口文件 index.js 的內容進行修改。 replaceLoader.js 文件的代碼具體以下:異步

module.exports = function(source) {
    const result = source.replace('monday', 'mondaylab');
    this.callback(null, result);
}
複製代碼

以上的代碼意思爲,將入口文件 index.js 文件中的 monday 替換爲 mondaylab 。這樣寫彷佛沒啥問題,可是你們有沒有想過,咱們有時候傳的屬性可能會很詭異,不必定每次都能像這樣以字符串的形式來替換。

因此,咱們引用 webpack 官方推薦的 loadertils 這個工具,來解決這個問題。

第一步: 安裝 loader-utils 插件。具體命令以下:

npm install loader-utils --save-dev
複製代碼

第二步: 改造 loader 文件。接下來,咱們對 replaceLoader.js 文件進行改造升級,具體代碼以下:

const loaderUtils = require('loader-utils');

//用function的緣由在於爲了業務層能夠調用this
//source爲引入文件的源代碼
module.exports = function(source) {
    //getOptions會自動地幫咱們分析this.query,而後把參數的全部內容放在options裏面去
    const options = loaderUtils.getOptions(this);

    const result = source.replace('monday', options.name);

    this.callback(null, result);
}
複製代碼

你們能夠看到,經過使用 loaderUtils 插件,間接地,調用 getOptions 方法,來自動的幫咱們分析 this.query ,從而取到咱們想要的內容。

值得注意的是,咱們還須要再瞭解一下 this.callback 的內容。

通常狀況下,若是咱們接收到了源代碼 source ,那麼如今咱們只能對源代碼作處理。可是呢,有的時候,咱們想要使用一些 sourceMap ,或者對源代碼分析好了以後,咱們不只想要返回源代碼,還要把 sourceMap 也帶回去。

由於咱們 return 的時候只能 return 一個參數,其他的一些額外的內容就帶不出去了。這個時候呢,咱們就須要 this.callback 來幫咱們把 sourceMap 給帶出去。所以,通常用 this.callback 來返回內容。

(3)引用loader

如今,咱們在 webpack.config.js 中,來引入咱們上面的 loader具體配置以下:

const path = require('path');

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    module: {
        rules: [{
            test: /\.js/,
            use: [
                {
                    loader: path.resolve(__dirname, './loaders/replaceLoader.js'),
                    //上面的options.name中的name
                    options: {
                        name: 'mondaylab'
                    }
                }   
            ]
        }]
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js' 
    }
}
複製代碼

經過以上方式,咱們寫了一個簡易的 loader ,這個 loader 實現了將 monday 替換爲 mondaylab 的功能。而且供咱們在 webpack 中使用本身書寫的 loader

(4)在loader裏面作一些異步的操做

好了如今,若是咱們想要給 loader 作一些異步操做,該怎麼實現呢?

在咱們所寫的 loader 當中,加入異步操做,那麼咱們須要調用官方提供給咱們的 this.async() 這個 API 來實現。如今,咱們來改造一下 replaceLoader.js 文件的代碼。具體代碼以下:

const loaderUtils = require('loader-utils');

module.exports = function(source) {

    const options = loaderUtils.getOptions(this);
    //調用this.async()這個API,來給異步代碼使用
    const callback = this.async();

    setTimeout(() => {
        const result = source.replace('monday', options.name);
        callback(null, result);
    }, 1000);
}
複製代碼

經過這種方式,咱們就能夠在 loader 中編寫異步代碼,來達到咱們想要的效果。

(5)loader路徑自定義

有一個很小的注意點就是,當咱們在配置 webpack.config.js 文件中, loader 的路徑時,每回都要 path.resolve 去尋找路徑文件。文件少的時候還好,但若是遇到多文件的時候呢?豈不是會很麻煩。

因此,咱們引用 resolveLoader 來簡化它。如今咱們在 webpack.config.js 文件中進行改造。具體配置以下:

const path = require('path');

module.exports = {
    // 先到node_modules中去找,找不到則去./loaders目錄下去找
    resolveLoader: {
        modules: ['node_modules', './loaders']
    },
    module: {
        rules: [{
            test: /\.js/,
            use: [
                {
                    loader: 'replaceLoader'
                }
            ]
        }]
    }
}
複製代碼

經過配置 resolveLoader ,來對文件文件目錄進行查找,從而簡化了路徑內容。

🎶2、如何編寫一個Plugin

1. 碎碎念

在講解 plugin 以前,咱們先來了解 loaderplugin 的區別。

當咱們在源代碼裏面,去引入一個新的 js 文件,或者是一個其餘格式的文件時,這個時候咱們能夠借用 loader ,來幫咱們處理咱們引用的 loader 文件。 loader 的做用就在於,幫助咱們處理引用的模塊

plugin 呢,是當咱們在作打包的時候,在某些具體時刻上,好比說,當咱們打包結束以後,咱們要生成一個 html 文件,這個時候,咱們就可使用一個 htmlWebpackPlugin 的插件。使用它以後,他就會在打包結束以後,幫咱們生成對應的 html 文件。

再好比,咱們要在打包以前,把 dist 目錄進行清空,這個時候咱們就可使用 cleanWebpackPlugin 來幫助咱們作這件事情。

因此, plugin 插件,在何時生效呢?

它在咱們打包過程當中的某些時刻裏,就是插件生效的場景

plugin 的編寫相對於 loader 來講,會難一點點。可是呢,若是有看過 webpack 源碼的小夥伴們可能會知道, webpack 的一些底層原理都是依據 plugin 來進行編寫的。因此,咱們仍是有必要來學習一下 plugin 的編寫。

下面就帶領你們來編寫一個簡易的 plugin ~

2. 項目結構

對於 webpackplugin 來講,它是是基於發佈者訂閱的設計模式,也能夠說是基於事件驅動來實現的。在這個事件驅動裏,代碼之間的執行,是經過事件來進行驅動的。

接下來,咱們就來寫一個簡易的 plugin

首先用一張圖,來看咱們的項目結構以下圖所示:

2-plugin項目結構.png

其中 plugins 文件夾下放置咱們想要寫的 plugin ,同時裏面的 copyright-webpack-plugin.js 文件放置咱們即將要寫的 plugin 的代碼邏輯。以後,index.js 文件是咱們的入口文件,放置咱們的業務邏輯。 webpack.config.js 文件放置關於 webpack 的相關配置,而 dist 文件夾內的內容,放置的是咱們經過 webpack 打包後,生成的打包文件。

3. 業務代碼編寫

(1)入口文件代碼

如今,咱們先來編寫入口文件 index.js 的代碼。具體代碼以下:

console.log('hello monday');
複製代碼

(2)編寫plugin

如今,咱們來編寫 plugin 的內容, copyright-webpack-plugin.js 文件的代碼具體以下:

class CopyrightWebpackPlugin {
    //編寫一個構造器
    constructor(options) {
         console.log(options)
     }

    apply(compiler) {
        //遇到同步時刻
        compiler.hooks.compile.tap('CopyrightWebpackPlugin',() => {
            console.log('compiler');
        });
		
        //遇到異步時刻
        //當要把代碼放到dist目錄以前,要走下面這個函數
        //Compilation存放打包的全部內容,Compilation.assets放置生成的內容
        compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (Compilation, cb) => {
            debugger;
            // 往代碼中增長一個文件,copyright.txt
            Compilation.assets['copyright.txt'] = {
                source: function() {
                    return 'copyright by monday';
                },
                size: function() {
                    return 19;
                }
            };
            cb();
        })
    }
}

module.exports = CopyrightWebpackPlugin;
複製代碼

上面的這個插件中想要實現的功能就是,獲取版權信息

(3)引用plugin

如今,咱們在 webpack.config.js 中,來引入咱們上面的 plugin具體配置以下:

const path = require('path');
const CopyrightWebpackPlugin = require('./plugins/copyright-webpack-plugin');

module.exports = {
    mode: 'development',
    entry: { 
        main: './src/index.js'
    },
    plugins: [
        new CopyrightWebpackPlugin({
            name: 'monday'
        })
    ],
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
}
複製代碼

經過上述代碼,咱們能夠了解到,在(2)中,咱們首先須要定義一個類,以後呢,在類中寫一個構造器和一個 apply() 方法來調用。而後呢,你們看到(3),經過 require 的方式,來進行 new 實例 ,實例化一個插件,從而在項目中使用這個插件。

最終,咱們項目進行打包時,就會生成一個 dist 目錄,而且在目錄下增長一個 copyright.txt 文件,而且文件中的內容就是 copyright by monday

💹3、結束語

在上面的文章中,講解了關於loader和plugin的基本編寫思路,以及如何在項目中對他們進行運用,相信你們對這一塊內容有了基礎的認識。

到這裏,loader和plugin的編寫講解就結束啦!但願對你們有幫助~

如文章有誤或有不理解的地方,歡迎小夥伴們評論區留言哦💬

本系列文章代碼已上傳至公衆號,後臺回覆關鍵詞 webpack 便可獲取~

🐣彩蛋 One More Thing

(:往期推薦

webpack入門核心知識👉萬字總結webpack的超入門核心知識

webpack入門進階知識👉萬字總結webpack入門進階知識

webpack實戰案例配置👉萬字總結webpack實戰案例配置

(:番外篇

  • 關注公衆號星期一研究室,第一時間關注優質文章,更多精選專欄待你解鎖~

  • 若是這篇文章對你有用,記得留個腳印jio再走哦~

  • 以上就是本文的所有內容!咱們下期見!👋👋👋

相關文章
相關標籤/搜索