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

淺談Webpack4 plugins 實(shí)現(xiàn)原理

 更新時(shí)間:2021年09月26日 15:41:40   作者:Hisen  
在wabpack 核心功能除了loader應(yīng)該就是plugins插件了,本文主要介紹了Webpack4 plugins 實(shí)現(xiàn)原理,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

前言

在 wabpack 中核心功能除了 loader 應(yīng)該就是 plugins 插件了,它是在webpack執(zhí)行過(guò)程中會(huì)廣播一系列事件,plugin 會(huì)監(jiān)聽(tīng)這些事件并通過(guò) webpack Api 對(duì)輸出文件做對(duì)應(yīng)的處理, 如 hmlt-webpack-plugin 就是對(duì)模板魔劍 index.html 進(jìn)行拷貝到 dist 目錄的

認(rèn)識(shí)

先來(lái)通過(guò)源碼來(lái)認(rèn)識(shí)一下 plugins 的基本結(jié)構(gòu)
https://github.com/webpack/webpack/blob/webpack-4/lib/Compiler.js 551行

// 創(chuàng)建一個(gè)編譯器
createChildCompiler(
  compilation,
  compilerName,
  compilerIndex,
  outputOptions,
  plugins // 里邊就有包含插件
) {

   // new 一個(gè) 編譯器
  const childCompiler = new Compiler(this.context);
  // 尋找存在的所有 plugins 插件
  if (Array.isArray(plugins)) {
    for (const plugin of plugins) {
       // 如果存在, 就調(diào)用 plugin 的 apply 方法
      plugin.apply(childCompiler);
    }
  }
  
  // 遍歷尋找 plugin 對(duì)應(yīng)的 hooks
  for (const name in this.hooks) {
    if (
      ![
        "make",
        "compile",
        "emit",
        "afterEmit",
        "invalid",
        "done",
        "thisCompilation"
      ].includes(name)
    ) {
    
      // 找到對(duì)應(yīng)的 hooks 并調(diào)用, 
      if (childCompiler.hooks[name]) {
        childCompiler.hooks[name].taps = this.hooks[name].taps.slice();
      }
    }
  }
 
 // .... 省略 ....

  return childCompiler;
}

通過(guò)上述源碼可以看出來(lái) plugin 本質(zhì)就是一個(gè)類(lèi), 首先就是 new 一個(gè) compiler 類(lèi),傳入當(dāng)前的上下文,然后判斷是否存在,存在則直接調(diào)用對(duì)應(yīng) plugin 的 apply 方法,然后再找到對(duì)應(yīng) plugin 調(diào)用的 hooks 事件流 , 發(fā)射給對(duì)應(yīng) hooks 事件
hooks 哪里來(lái)的 ?

https://github.com/webpack/webpack/blob/webpack-4/lib/Compiler.js 42行

// 上述的 Compiler 類(lèi)繼承自 Tapable 類(lèi),而 Tapable 就定義了這些 hooks 事件流
class Compiler extends Tapable {
 constructor(context) {
            super();
            this.hooks = {
                    /** @type {SyncBailHook<Compilation>} */
                    shouldEmit: new SyncBailHook(["compilation"]),
                    /** @type {AsyncSeriesHook<Stats>} */
                    done: new AsyncSeriesHook(["stats"]),
                    /** @type {AsyncSeriesHook<>} */
                    additionalPass: new AsyncSeriesHook([]),
                    /** @type {AsyncSeriesHook<Compiler>} */
                    beforeRun: new AsyncSeriesHook(["compiler"]),
                    /** @type {AsyncSeriesHook<Compiler>} */
                    run: new AsyncSeriesHook(["compiler"]),
                    /** @type {AsyncSeriesHook<Compilation>} */
                    emit: new AsyncSeriesHook(["compilation"]),
                    /** @type {AsyncSeriesHook<string, Buffer>} */
                    assetEmitted: new AsyncSeriesHook(["file", "content"]),
                    /** @type {AsyncSeriesHook<Compilation>} */
                    afterEmit: new AsyncSeriesHook(["compilation"]),

                    /** @type {SyncHook<Compilation, CompilationParams>} */
                    thisCompilation: new SyncHook(["compilation", "params"]),
                    /** @type {SyncHook<Compilation, CompilationParams>} */
                    compilation: new SyncHook(["compilation", "params"]),
                    /** @type {SyncHook<NormalModuleFactory>} */
                    normalModuleFactory: new SyncHook(["normalModuleFactory"]),
                    /** @type {SyncHook<ContextModuleFactory>}  */
                    contextModuleFactory: new SyncHook(["contextModulefactory"]),

                    /** @type {AsyncSeriesHook<CompilationParams>} */
                    beforeCompile: new AsyncSeriesHook(["params"]),
                    /** @type {SyncHook<CompilationParams>} */
                    compile: new SyncHook(["params"]),
                    /** @type {AsyncParallelHook<Compilation>} */
                    make: new AsyncParallelHook(["compilation"]),
                    /** @type {AsyncSeriesHook<Compilation>} */
                    afterCompile: new AsyncSeriesHook(["compilation"]),

                    /** @type {AsyncSeriesHook<Compiler>} */
                    watchRun: new AsyncSeriesHook(["compiler"]),
                    /** @type {SyncHook<Error>} */
                    failed: new SyncHook(["error"]),
                    /** @type {SyncHook<string, string>} */
                    invalid: new SyncHook(["filename", "changeTime"]),
                    /** @type {SyncHook} */
                    watchClose: new SyncHook([]),

                    /** @type {SyncBailHook<string, string, any[]>} */
                    infrastructureLog: new SyncBailHook(["origin", "type", "args"]),

                    // TODO the following hooks are weirdly located here
                    // TODO move them for webpack 5
                    /** @type {SyncHook} */
                    environment: new SyncHook([]),
                    /** @type {SyncHook} */
                    afterEnvironment: new SyncHook([]),
                    /** @type {SyncHook<Compiler>} */
                    afterPlugins: new SyncHook(["compiler"]),
                    /** @type {SyncHook<Compiler>} */
                    afterResolvers: new SyncHook(["compiler"]),
                    /** @type {SyncBailHook<string, Entry>} */
                    entryOption: new SyncBailHook(["context", "entry"])
            };
            
            // TODO webpack 5 remove this
            this.hooks.infrastructurelog = this.hooks.infrastructureLog;
               
            // 通過(guò) tab 調(diào)用對(duì)應(yīng)的 comiler 編譯器,并傳入一個(gè)回調(diào)函數(shù)
            this._pluginCompat.tap("Compiler", options => {
                    switch (options.name) {
                            case "additional-pass":
                            case "before-run":
                            case "run":
                            case "emit":
                            case "after-emit":
                            case "before-compile":
                            case "make":
                            case "after-compile":
                            case "watch-run":
                                    options.async = true;
                                    break;
                    }
            });
            // 下方省略 ......
  }

好了,了解過(guò)基本的結(jié)構(gòu)之后,就可以推理出 plugin 基本的結(jié)構(gòu)和用法了,就是下邊這樣

// 定義一個(gè) plugins 類(lèi)   
class MyPlugins {
    // 上邊有說(shuō) new 一個(gè)編譯器實(shí)例,會(huì)執(zhí)行實(shí)例的 apply 方法,傳入對(duì)應(yīng)的 comiler 實(shí)例
    apply (compiler) {
        // 調(diào)用 new 出來(lái) compiler 實(shí)例下的 hooks 事件流,通過(guò) tab 觸發(fā),并接收一個(gè)回調(diào)函數(shù)
        compiler.hooks.done.tap('一般為插件昵稱(chēng)', (默認(rèn)接收參數(shù)) => {
            console.log('進(jìn)入執(zhí)行體');
        })
    }
}
// 導(dǎo)出
module.exports = MyPlugins

ok, 以上就是一個(gè)簡(jiǎn)單的 模板 ,我們來(lái)試試內(nèi)部的鉤子函數(shù),是否會(huì)如愿以?xún)數(shù)谋徽{(diào)用和觸發(fā)

配置 webpack

let path = require('path')
let DonePlugin = require('./plugins/DonePlugins')
let AsyncPlugins = require('./plugins/AsyncPlugins')

module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'build.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new DonePlugin(),    // 內(nèi)部同步 hooks
        new AsyncPlugins()   // 內(nèi)部異步 hooks
    ]
}

同步 plugin 插件模擬調(diào)用

class DonePlugins {
    apply (compiler) {
        compiler.hooks.done.tap('DonePlugin', (stats) => {
            console.log('執(zhí)行: 編譯完成');
        })
    }
}

module.exports = DonePlugins

異步 plugin 插件模擬調(diào)用

class AsyncPlugins {
    apply (compiler) {
        compiler.hooks.emit.tapAsync('AsyncPlugin', (complete, callback) => {
            setTimeout(() => {
                console.log('執(zhí)行:文件發(fā)射出來(lái)');
                callback()
            }, 1000)
        })
    }
}

module.exports = AsyncPlugins

最后編譯 webpack 可以看到編譯控制臺(tái),分別打印 執(zhí)行: 編譯完成,執(zhí)行:文件發(fā)射出來(lái),說(shuō)明這樣是可以調(diào)用到 hooks 事件流的,并且可以觸發(fā)。

實(shí)踐出真知

了解過(guò)基本結(jié)構(gòu)和使用的方式了,現(xiàn)在來(lái)手寫(xiě)一個(gè) plugin 插件,嗯,就來(lái)一個(gè)文件說(shuō)明插件吧,我們?nèi)粘4虬梢源虬粋€(gè) xxx.md 文件到 dist 目錄,來(lái)做一個(gè)打包說(shuō)明,就來(lái)是實(shí)現(xiàn)這么一個(gè)小功能

文件說(shuō)明插件

class FileListPlugin {
    // 初始化,獲取文件的名稱(chēng)
    constructor ({filename}) {
        this.filename = filename
    }
    // 同樣的模板形式,定義 apply 方法
    apply (compiler) {
        compiler.hooks.emit.tap('FileListPlugin', (compilation) => {
            // assets 靜態(tài)資源,可以打印出  compilation 參數(shù),還有很多方法和屬性
            let assets = compilation.assets;
            
            // 定義輸出文檔結(jié)構(gòu)
            let content = `## 文件名  資源大小\r\n`
            
            // 遍歷靜態(tài)資源,動(dòng)態(tài)組合輸出內(nèi)容
            Object.entries(assets).forEach(([filename, stateObj]) => {
                content += `- ${filename}    ${stateObj.size()}\r\n`
            })
            
            // 輸出資源對(duì)象
            assets[this.filename] = {
                source () {
                    return content;
                },
                size () {
                    return content.length
                }
            }
            
        })
    }
}
// 導(dǎo)出
module.exports = FileListPlugin

webpack 配置

let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin')
// plugins 目錄與node_modules 同級(jí), 自定義 plugins , 與 loader 類(lèi)似
let FileListPlugin = require('./plugins/FileListPlugin')

module.exports = {
    mode: 'development',
    entry: './src/index.js',
    output: {
        filename: 'build.js',
        path: path.resolve(__dirname, 'dist')
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html'
        }),
        new FileListPlugin({
            filename: 'list.md'
        })
    ]
}

ok,通過(guò)以上配置,我們?cè)俅虬臅r(shí)候就可以看到,每次打包在 dist 目錄就會(huì)出現(xiàn)一個(gè) xxx.md 文件,而這個(gè)文件的內(nèi)容就是我們上邊的 content

到此這篇關(guān)于淺談Webpack4 plugins 實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Webpack4 plugins 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論