最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

基礎(chǔ)理論知識

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

2、webpack 在使用插件時,會初始化一個插件實例并調(diào)用其原型對象上的 apply 方法,apply 方法接收一個 compiler 參數(shù),下文將詳細介紹這個參數(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)建。每當檢測到一個文件變化,就會創(chuàng)建一個新的 compilation,從而生成一組新的編譯資源。

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

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

小結(jié)

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

compilation 是對于某個模塊而言的,它可以更加精細地處理各個模塊的構(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ā)生了改動,會進行一次新的構(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)一化就可以進行對比了。篩選出需要刪除的文件,用 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)有文件進行對比
        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)文章

最新評論

汝州市| 正安县| 曲阜市| 平罗县| 葫芦岛市| 新龙县| 阳西县| 永清县| 石门县| 岐山县| 昌黎县| 江油市| 肥乡县| 东乌珠穆沁旗| 台中县| 上蔡县| 江北区| 芦山县| 绥江县| 曲周县| 贺兰县| 青龙| 平阴县| 铜梁县| 社旗县| 杭州市| 梨树县| 麟游县| 崇阳县| 赤城县| 天全县| 全椒县| 沙河市| 平江县| 赤壁市| 太原市| 五华县| 赞皇县| 上林县| 文成县| 惠来县|