广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >浅谈Webpack4 plugins 实现原理
  • 824
分享到

浅谈Webpack4 plugins 实现原理

2024-04-02 19:04:59 824人浏览 泡泡鱼
摘要

目录前言认识实践出真知前言 在 wabpack 中核心功能除了 loader 应该就是 plugins 插件了,它是在webpack执行过程中会广播一系列事件,plugin 会监听

前言

在 wabpack 中核心功能除了 loader 应该就是 plugins 插件了,它是在webpack执行过程中会广播一系列事件,plugin 会监听这些事件并通过 WEBpack api 对输出文件做对应的处理, 如 hmlt-webpack-plugin 就是对模板魔剑 index.html 进行拷贝到 dist 目录的

认识

先来通过源码来认识一下 plugins 的基本结构
https://GitHub.com/webpack/webpack/blob/webpack-4/lib/Compiler.js 551行


// 创建一个编译器
createChildCompiler(
  compilation,
  compilerName,
  compilerIndex,
  outputOptions,
  plugins // 里边就有包含插件
) {

   // new 一个 编译器
  const childCompiler = new Compiler(this.context);
  // 寻找存在的所有 plugins 插件
  if (Array.isArray(plugins)) {
    for (const plugin of plugins) {
       // 如果存在, 就调用 plugin 的 apply 方法
      plugin.apply(childCompiler);
    }
  }
  
  // 遍历寻找 plugin 对应的 hooks
  for (const name in this.hooks) {
    if (
      ![
        "make",
        "compile",
        "emit",
        "afterEmit",
        "invalid",
        "done",
        "thisCompilation"
      ].includes(name)
    ) {
    
      // 找到对应的 hooks 并调用, 
      if (childCompiler.hooks[name]) {
        childCompiler.hooks[name].taps = this.hooks[name].taps.slice();
      }
    }
  }
 
 // .... 省略 ....

  return childCompiler;
}

通过上述源码可以看出来 plugin 本质就是一个类, 首先就是 new 一个 compiler 类,传入当前的上下文,然后判断是否存在,存在则直接调用对应 plugin 的 apply 方法,然后再找到对应 plugin 调用的 hooks 事件流 , 发射给对应 hooks 事件
hooks 哪里来的 ?

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


// 上述的 Compiler 类继承自 Tapable 类,而 Tapable 就定义了这些 hooks 事件流
class Compiler extends Tapable {
 constructor(context) {
            super();
            this.hooks = {
                    
                    shouldEmit: new SyncBailHook(["compilation"]),
                    
                    done: new AsyncSeriesHook(["stats"]),
                    
                    additionalPass: new AsyncSeriesHook([]),
                    
                    beforeRun: new AsyncSeriesHook(["compiler"]),
                    
                    run: new AsyncSeriesHook(["compiler"]),
                    
                    emit: new AsyncSeriesHook(["compilation"]),
                    
                    assetEmitted: new AsyncSeriesHook(["file", "content"]),
                    
                    afterEmit: new AsyncSeriesHook(["compilation"]),

                    
                    thisCompilation: new SyncHook(["compilation", "params"]),
                    
                    compilation: new SyncHook(["compilation", "params"]),
                    
                    nORMalModuleFactory: new SyncHook(["normalModuleFactory"]),
                    
                    contextModuleFactory: new SyncHook(["contextModulefactory"]),

                    
                    beforeCompile: new AsyncSeriesHook(["params"]),
                    
                    compile: new SyncHook(["params"]),
                    
                    make: new AsyncParallelHook(["compilation"]),
                    
                    afterCompile: new AsyncSeriesHook(["compilation"]),

                    
                    watchRun: new AsyncSeriesHook(["compiler"]),
                    
                    failed: new SyncHook(["error"]),
                    
                    invalid: new SyncHook(["filename", "changeTime"]),
                    
                    watchClose: new SyncHook([]),

                    
                    infrastructureLog: new SyncBailHook(["origin", "type", "args"]),

                    // TODO the following hooks are weirdly located here
                    // TODO move them for webpack 5
                    
                    environment: new SyncHook([]),
                    
                    afterEnvironment: new SyncHook([]),
                    
                    afterPlugins: new SyncHook(["compiler"]),
                    
                    afterResolvers: new SyncHook(["compiler"]),
                    
                    entryOption: new SyncBailHook(["context", "entry"])
            };
            
            // TODO webpack 5 remove this
            this.hooks.infrastructurelog = this.hooks.infrastructureLog;
               
            // 通过 tab 调用对应的 comiler 编译器,并传入一个回调函数
            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;
                    }
            });
            // 下方省略 ......
  }

好了,了解过基本的结构之后,就可以推理出 plugin 基本的结构和用法了,就是下边这样


// 定义一个 plugins 类   
class MyPlugins {
    // 上边有说 new 一个编译器实例,会执行实例的 apply 方法,传入对应的 comiler 实例
    apply (compiler) {
        // 调用 new 出来 compiler 实例下的 hooks 事件流,通过 tab 触发,并接收一个回调函数
        compiler.hooks.done.tap('一般为插件昵称', (默认接收参数) => {
            console.log('进入执行体');
        })
    }
}
// 导出
module.exports = MyPlugins

ok, 以上就是一个简单的 模板 ,我们来试试内部的钩子函数,是否会如愿以偿的被调用和触发

配置 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(),    // 内部同步 hooks
        new AsyncPlugins()   // 内部异步 hooks
    ]
}

同步 plugin 插件模拟调用


class DonePlugins {
    apply (compiler) {
        compiler.hooks.done.tap('DonePlugin', (stats) => {
            console.log('执行: 编译完成');
        })
    }
}

module.exports = DonePlugins

异步 plugin 插件模拟调用


class AsyncPlugins {
    apply (compiler) {
        compiler.hooks.emit.tapAsync('AsyncPlugin', (complete, callback) => {
            setTimeout(() => {
                console.log('执行:文件发射出来');
                callback()
            }, 1000)
        })
    }
}

module.exports = AsyncPlugins

最后编译 webpack 可以看到编译控制台,分别打印 执行: 编译完成,执行:文件发射出来,说明这样是可以调用到 hooks 事件流的,并且可以触发。

实践出真知

了解过基本结构和使用的方式了,现在来手写一个 plugin 插件,嗯,就来一个文件说明插件吧,我们日常打包,可以打包一个 xxx.md 文件到 dist 目录,来做一个打包说明,就来是实现这么一个小功能

文件说明插件


class FileListPlugin {
    // 初始化,获取文件的名称
    constructor ({filename}) {
        this.filename = filename
    }
    // 同样的模板形式,定义 apply 方法
    apply (compiler) {
        compiler.hooks.emit.tap('FileListPlugin', (compilation) => {
            // assets 静态资源,可以打印出  compilation 参数,还有很多方法和属性
            let assets = compilation.assets;
            
            // 定义输出文档结构
            let content = `## 文件名  资源大小\r\n`
            
            // 遍历静态资源,动态组合输出内容
            Object.entries(assets).forEach(([filename, stateObj]) => {
                content += `- ${filename}    ${stateObj.size()}\r\n`
            })
            
            // 输出资源对象
            assets[this.filename] = {
                source () {
                    return content;
                },
                size () {
                    return content.length
                }
            }
            
        })
    }
}
// 导出
module.exports = FileListPlugin

webpack 配置


let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin')
// plugins 目录与node_modules 同级, 自定义 plugins , 与 loader 类似
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,通过以上配置,我们再打包的时候就可以看到,每次打包在 dist 目录就会出现一个 xxx.md 文件,而这个文件的内容就是我们上边的 content

到此这篇关于浅谈Webpack4 plugins 实现原理的文章就介绍到这了,更多相关Webpack4 plugins 内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 浅谈Webpack4 plugins 实现原理

本文链接: https://www.lsjlt.com/news/136785.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 浅谈Webpack4 plugins 实现原理
    目录前言认识实践出真知前言 在 wabpack 中核心功能除了 loader 应该就是 plugins 插件了,它是在webpack执行过程中会广播一系列事件,plugin 会监听...
    99+
    2022-11-12
  • 浅谈Vue插槽实现原理
    目录一、样例代码二、透过现象看本质三、实现原理四、父组件编译阶段五、父组件生成渲染方法六、父组件生成VNode七、子组件状态初始化八、子组件编译阶段九、子组件生成渲染方法十、使用技巧...
    99+
    2022-11-12
  • 浅谈React底层实现原理
    目录1. props,state与render函数关系,数据和页面如何实现互相联动?2. React中的虚拟DOM常规思路改良思路(仍使用DOM)React的思路深入理解虚拟DOM3...
    99+
    2022-11-13
  • 浅谈SpringSecurity基本原理
    目录一、SpringSecurity 本质二、典型过滤器2.1 FilterSecurityInterceptor2.2 ExceptionTranslationFilter2.3 ...
    99+
    2022-11-12
  • 浅谈servlet3异步原理与实践
    一、什么是Servletservlet 是基于 Java 的 Web 组件,由容器进行管理,来生成动态内容。像其他基于 Java 的组件技术一样,servlet 也是基于平台无关的 Java 类格式,被编译为平台无关的字节码,可以被基于 J...
    99+
    2023-05-31
    servlet 异步
  • 浅谈express 中间件机制及实现原理
    简介 中间件机制可以让我们在一个给定的流程中添加一个处理步骤,从而对这个流程的输入或者输出产生影响,或者产生一些中作用、状态,或者拦截这个流程。中间件机制和tomcat的过滤器类似,这两者都属于责任链模式...
    99+
    2022-06-04
    浅谈 中间件 原理
  • 浅谈mysql join底层原理
    目录join算法驱动表和非驱动表的区别1、Simple Nested-Loop Join,简单嵌套-无索引的情况2、Index Nested-Loop Join-有索引的情况3、Block Nested-Loop J...
    99+
    2022-05-30
    mysql join底层原理 mysql join
  • 浅谈Spring Session工作原理
    目录1、引入背景2、使用方法3、工作流程4、缓存机制5、事件订阅6、总结1、引入背景 HTTP协议本身是无状态的,为了保存会话信息,浏览器Cookie通过SessionID标识会话请...
    99+
    2022-11-12
  • 浅谈JMeter engine启动原理
    目录一、简介二、配置简介三、开始原理讲解四、JMeter 引擎启动链路图一、简介 本文主要介绍jmeter在控制台在点击执行之后底层所做的一些主要事情及内容,由于便于断点调试采用GU...
    99+
    2022-11-12
  • 浅谈MySQL之浅入深出页原理
    目录一、页的概览二、Infimum 和 Supremum三、使用Page Directory四、页的真实面貌4.1、File Header4.2、Page Header4.3、Infimum & Suprem...
    99+
    2022-05-19
    MySQL 页原理
  • 怎么浅谈安卓apk加固原理和实现
    今天就跟大家聊聊有关怎么浅谈安卓apk加固原理和实现,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。下面主要向大家介绍apk加固原理和简单实现。一、apk常见加固方式(1)代码层级加密...
    99+
    2023-06-04
  • 浅谈SpringBoot实现自动装配的方法原理
    目录1. 什么是自动装配2. 自动装配的原理3. 自动装配的步骤4. 自定义自动配置5.代码案例总结1. 什么是自动装配 在传统的Spring框架中,我们需要手动配置和管理Bean的...
    99+
    2023-05-20
    SpringBoot实现自动装配 SpringBoot自动装配
  • 浅谈Java中的atomic包实现原理及应用
    1.同步问题的提出假设我们使用一个双核处理器执行A和B两个线程,核1执行A线程,而核2执行B线程,这两个线程现在都要对名为obj的对象的成员变量i进行加1操作,假设i的初始值为0,理论上两个线程运行后i的值应该变成2,但实际上很有可能结果为...
    99+
    2023-05-30
    java atomic 原理
  • 浅谈Node Inspector 代理实现
    背景 平时做 node 开发的时候,通过 node inspector 来进行断点调试是一个很常用的 debug 方式。但是有几个问题会导致我们的调试效率降低。 问题一:当使用 vscode 进行断点调试时...
    99+
    2022-06-04
    浅谈 Node Inspector
  • 浅谈线性表的原理及简单实现方法
    一、线性表原理:零个或多个同类数据元素的有限序列原理图:特点 :有序性有限性同类型元素第一个元素无前驱,最后一个元素无后继,中间的元素有一个前驱并且有一个后继线性表是一种逻辑上的数据结构,在物理上一般有两种实现 顺序实现和链表实现二、基于数...
    99+
    2023-05-31
    线性表
  • 浅谈消息队列的原理
    什么是消息队列 这样的场景你一定不陌生:小王到M记点餐之后,服务员给了他一个号牌,并让他在柜台桌子前方等待叫号取餐。每个人都按照自己付款拿到的号牌顺序排队等叫号。即使店里人再多,也不会显得没有秩序。在上述场...
    99+
    2022-10-18
  • 浅谈springboot自动装配原理
    目录一、SpringBootApplication二、案例三、Condition四、案例升级五、小结一、SpringBootApplication @Target(Element...
    99+
    2022-11-12
  • 浅谈swoole的作用与原理
    目录PHP 中的 Node ?Swoole 到底是什么?如何让它运行?使用 Docker 运行 SwooleSwoole 可以做什么?基于 Swoole 实现 HTTP 服务HTTP...
    99+
    2022-11-12
  • 浅谈vue实现双向事件绑定v-model的原理
    目录解释: 总结 补充 与js或者jquery直接改变操作dom不同,vue使用v-model实现数据的双向绑定,它会根据控件类型自动选取正确的方法来更新元素。 v-model就是v...
    99+
    2022-11-12
  • 浅谈JavaIO之try with底层原理
    目录IO关闭的问题使用try with resourcetry with resource的原理自定义resourceIO关闭的问题 最近写了一个例子,读取一个文件没有问题,但是读取...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作