欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Webpack簡單實現(xiàn)兩個自定義插件詳解

 更新時間:2024年04月24日 11:03:11   作者:土豆蛋蛋  
這篇文章主要為大家詳細(xì)介紹了Webpack簡單實現(xiàn)兩個自定義插件的相關(guān)知識,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

基礎(chǔ)理論知識

1、插件的本質(zhì)是一個函數(shù)或一個;

2、webpack 在使用插件時,會初始化一個插件實例并調(diào)用其原型對象上的 apply 方法,apply 方法接收一個 compiler 參數(shù),下文將詳細(xì)介紹這個參數(shù)。

函數(shù)實例

    function MyPlugin (options) {
    }
    MyPlugin.prototype.apply = compiler => {
    };
    module.exports = MyPlugin;

類實例

    class MyPlugin {
        constructor (options) {
        }
        apply (compiler) {
        }
    }
    module.exports = MyPlugin;

compiler 和 compilation

compiler

上文提到的 apply 方法中接收的 compiler 對象代表了完整的 webpack 環(huán)境配置。

這個對象在啟動 webpack 時被一次性建立,并配置好所有可操作的設(shè)置,包括 options,loader 和 plugin。

可以簡單地把它理解為 webpack 實例,使用它來訪問 webpack 的主環(huán)境和配置信息。

另外,compiler 對象暴露了很多生命周期的鉤子,通過如下方式使用:

    compiler.hooks.someHook.tap("MyPlugin", params => {
        /* ... */
    });

鉤子的訪問方式并不固定為 tap,這取決于鉤子的類型,主要分為同步和異步,訪問的方式有:tapAsync, tapPromise 等。

compilation

compilation 對象是從 compiler 鉤子的回調(diào)函數(shù)中傳遞回來的。

compilation 對象代表了一次資源版本構(gòu)建。每當(dāng)檢測到一個文件變化,就會創(chuàng)建一個新的 compilation,從而生成一組新的編譯資源。

一個 compilation 對象表現(xiàn)了當(dāng)前的模塊資源、編譯生成資源、變化的文件、以及被跟蹤依賴的狀態(tài)信息。

compilation 對象也暴露了很多生命周期的鉤子,以供插件做自定義處理時選擇使用。訪問方式與 compiler 相同,此處不再贅述。

小結(jié)

compiler 是一個全局的對象,是一整個構(gòu)建的過程,可以訪問 webpack 的環(huán)境和配置。

compilation 是對于某個模塊而言的,它可以更加精細(xì)地處理各個模塊的構(gòu)建過程。

簡單實現(xiàn)自定義插件

官方文檔中給了一個 File List Plugin 自定義插件的示例,該插件主要展示了如何獲取構(gòu)建過程中的資源。

除此之外,我們再來實現(xiàn)兩個簡單的自定義插件。

Watch Plugin

這個插件的作用是:在 webpack 的監(jiān)視(watch)模式下,輸出每次修改變更的資源文件信息。

在查閱官方文檔后,發(fā)現(xiàn)有個 watchRun 的鉤子很符合我們的需求:“在監(jiān)聽模式下,一個新的 compilation 觸發(fā)之后,但在 compilation 實際開始之前執(zhí)行。”。

也就是說,項目發(fā)生了改動,會進(jìn)行一次新的構(gòu)建,生成一個新的 compilation,并且在這個 compilation 執(zhí)行之前觸發(fā)。

watchRun 的訪問方式是 tapAsync,所以除了接收 compiler 參數(shù)外,還會接收一個回調(diào)函數(shù),我們需要在邏輯執(zhí)行完畢后調(diào)用這個回調(diào)函數(shù)。

代碼如下:

/**
 * 在 webpack 的 watch 模式下觸發(fā)
 */
class WatchPlugin {
    
    apply (webpackCompiler) {
        
        // watchRun - 在監(jiān)聽模式下觸發(fā),在一個 compilation 出現(xiàn)后,在 compilation 執(zhí)行前觸發(fā)
        webpackCompiler.hooks.watchRun.tapAsync("WatchPlugin", (compiler, callback) => {
            console.log(" 監(jiān)聽到了! ");

            const mtimes = compiler.watchFileSystem.watcher.mtimes;
            if (!mtimes) return;
            // 通過正則處理,避免顯示 node_modules 文件夾下依賴的變化
            const mtimesKeys = Object.keys(mtimes).filter(path => !/(node_modules)/.test(path));
            if (mtimesKeys.length) {
                console.log(` 本次改動了 ${mtimesKeys.length} 個文件,路徑為:\n `, mtimes);
            }

            callback();
        });

        // watchClose - 在一個監(jiān)聽中的 compilation 結(jié)束時觸發(fā)
        webpackCompiler.hooks.watchClose.tap("WatchPlugin", () => {
            console.log(" 監(jiān)聽結(jié)束,再見! ");
        });

    }

}

module.exports = WatchPlugin;

Clean Plugin

模仿 clean-webpack-plugin 實現(xiàn)一個每次構(gòu)建時將上一次構(gòu)建結(jié)果中不再需要的文件刪除。

梳理邏輯

項目文件改動后,構(gòu)建結(jié)果的文件 hash 值會發(fā)生變化,此時將舊文件刪除;如果沒有發(fā)生改動,則無需刪除。

實現(xiàn)邏輯

1、考慮在什么時機執(zhí)行邏輯?

要根據(jù) hash 判斷文件是否發(fā)生了變化,所以要拿到新、舊文件,那么可以在新的一次構(gòu)建完成后執(zhí)行,此時可以獲取新構(gòu)建出的文件。

查閱文檔后,compiler.done 這個鉤子會在 compilation 完成后觸發(fā),符合我們的需求。

2、如何獲取上一次構(gòu)建出的舊文件?

先獲取 output 的路徑,根據(jù)路徑就可以獲取到 output 文件夾下所有的文件了。這個操作要放在構(gòu)建開始之前。

上文說過 compiler 可以訪問 webpack 的環(huán)境與配置,因此通過 compiler 可以獲取 output 的路徑:

apply (compiler) {
    const outputPath = compiler.options.output.path;
}

獲取 output 文件夾中的文件,可以通過 fs.readdirSync 方法和 fs.statSync 方法。

前者是用于讀取某一路徑下所有的文件名,包括文件和文件夾。

后者是用于判斷某一路徑是文件還是文件夾。

使用這兩個方法,我們可以遞歸獲取 output 文件夾以及其子級文件夾下所有的文件

/**
 * 獲取文件夾下所有的文件名(包括子級文件夾中的文件)
 * @param {string} dir 文件夾路徑
 */
readAllFiles (dir) {
    let fileList = [];

    const files = fs.readdirSync(dir);
    files.forEach(file => {
        const filePath = path.join(dir, file);
        const stat = fs.statSync(filePath);
        if (stat.isDirectory()) {
            fileList = fileList.concat(this.readAllFiles(filePath));
        } else {
            fileList.push(filePath);
        }
    });

    return fileList;
}

3、如何獲取新構(gòu)建出的文件?

compiler.done 這個鉤子的回調(diào)函數(shù)接收一個參數(shù) stat,通過 stat 可以獲取到最新構(gòu)建出的 assets。

這幾個問題解決后,我們將新、舊文件路徑統(tǒng)一化就可以進(jìn)行對比了。篩選出需要刪除的文件,用 fs.unlinkSync 方法就可以直接刪除了。

代碼如下:

const fs = require("fs");
const path = require("path");

/** 每次編譯時刪除上一次編譯結(jié)果中不再需要的文件 */
class CleanPlugin {

    constructor (options) {
        this.options = options;
    }

    apply (compiler) {
        const pluginName = CleanPlugin.name;

        // 編譯輸出文件的路徑,根據(jù)此路徑可獲取對應(yīng)目錄下的所有文件
        const outputPath = compiler.options.output.path;
        const outputPathPrefix = path.basename(outputPath);
        const oldFiles = this.readAllFiles(outputPath);

        // console.log(" old files ", oldFiles);

        // done - 完成新的編譯后執(zhí)行,此時能獲取新的輸出文件與現(xiàn)有文件進(jìn)行對比
        compiler.hooks.done.tap(pluginName, stats => {

            // 新的一次編譯完成后的輸出文件的相對路徑
            const newFiles = stats.toJson().assets.map(assets => fs.realpathSync(`${outputPathPrefix}\\${assets.name}`));

            // console.log(" new files ", newFiles);

            // 新舊文件對比,篩選出需要刪除的文件
            const removeFiles = [];
            oldFiles.forEach(oldFile => {
                if (newFiles.indexOf(oldFile) === -1) {
                    removeFiles.push(oldFile);
                }
            });

            // 刪除文件
            removeFiles.forEach(removeFile => fs.unlinkSync(removeFile));
        });
    }

    /**
     * 獲取文件夾下所有的文件名(包括子級文件夾中的文件)
     * @param {string} dir 文件夾路徑
     */
    readAllFiles (dir) {
        let fileList = [];

        const files = fs.readdirSync(dir);
        files.forEach(file => {
            const filePath = path.join(dir, file);
            const stat = fs.statSync(filePath);
            if (stat.isDirectory()) {
                fileList = fileList.concat(this.readAllFiles(filePath));
            } else {
                fileList.push(filePath);
            }
        });

        return fileList;
    }

}

module.exports = CleanPlugin;

到此這篇關(guān)于Webpack簡單實現(xiàn)兩個自定義插件詳解的文章就介紹到這了,更多相關(guān)Webpack自定義插件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • js判斷移動端橫豎屏視口檢測實現(xiàn)的幾種方法

    js判斷移動端橫豎屏視口檢測實現(xiàn)的幾種方法

    最近做歌一個小項目,但是要放到我們的app上,然而需要橫豎屏使用不同的樣式,本文就來介紹一下js判斷移動端橫豎屏視口檢測實現(xiàn)的幾種方法,感興趣的可以了解一下
    2021-07-07
  • uniapp實現(xiàn)下拉刷新與上拉觸底加載功能的示例代碼

    uniapp實現(xiàn)下拉刷新與上拉觸底加載功能的示例代碼

    這篇文章主要記錄一下uniapp實現(xiàn)下拉刷新與上拉觸底加載功能的示例代碼,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-04-04
  • javascript在事件監(jiān)聽方面的兼容性小結(jié)

    javascript在事件監(jiān)聽方面的兼容性小結(jié)

    javascript 在事件監(jiān)聽方面的兼容性總結(jié),注意是由于多個瀏覽器的不一致,導(dǎo)致大家在js書寫時需要考慮多個瀏覽器的兼容性。
    2010-04-04
  • JavaScript實現(xiàn)音樂導(dǎo)航效果

    JavaScript實現(xiàn)音樂導(dǎo)航效果

    這篇文章主要為大家詳細(xì)介紹了JavaScript實現(xiàn)音樂導(dǎo)航效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • uniapp實現(xiàn)日期時間選擇器

    uniapp實現(xiàn)日期時間選擇器

    這篇文章主要為大家詳細(xì)介紹了uniapp實現(xiàn)日期時間選擇器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • JavaScript實現(xiàn)網(wǎng)頁tab欄效果制作

    JavaScript實現(xiàn)網(wǎng)頁tab欄效果制作

    這篇文章主要為大家詳細(xì)介紹了JavaScript實現(xiàn)網(wǎng)頁tab欄效果制作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • 響應(yīng)式表格之固定表頭的簡單實現(xiàn)

    響應(yīng)式表格之固定表頭的簡單實現(xiàn)

    下面小編就為大家?guī)硪黄憫?yīng)式表格之固定表頭的簡單實現(xiàn)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-08-08
  • javascript實現(xiàn)劃詞標(biāo)記劃詞搜索功能修正版

    javascript實現(xiàn)劃詞標(biāo)記劃詞搜索功能修正版

    javascript實現(xiàn)劃詞標(biāo)記劃詞搜索功能修正版...
    2006-12-12
  • 微信小程序如何處理token過期問題

    微信小程序如何處理token過期問題

    最近再做個項目,需要檢查token過期,跳轉(zhuǎn)到登錄頁面,要求用戶重新登錄,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • 詳解JavaScript中的作用域

    詳解JavaScript中的作用域

    作用域是JavaScript中一個重要的概念,它決定了變量和函數(shù)在代碼中的可訪問性和可見性,了解JavaScript的作用域?qū)τ诰帉懜咝А⒖删S護(hù)的代碼至關(guān)重要,本文將深入介紹JavaScript作用域相關(guān)的知識點,其中包括作用域類型,作用域鏈,變量提升以及閉包等
    2023-08-08

最新評論