Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >10 分钟了解 webpack 核心内容

10 分钟了解 webpack 核心内容

作者头像
奋飛
发布于 2021-08-30 01:45:40
发布于 2021-08-30 01:45:40
45600
代码可运行
举报
文章被收录于专栏:Super 前端Super 前端
运行总次数:0
代码可运行

10 分钟了解 webpack 核心内容

直接上手稿了

Tapable 是 webpack 核心工具之一,提供了插件接口。webpack 中许多对象扩展自 Tapable 类(如,负责编译的 Compiler 和负责创建 bundles 的 Compilation)。这个类暴露 tap, tapAsynctapPromise 方法,可以使用这些方法,注入自定义的构建步骤,这些步骤将在整个编译过程中不同时机触发。

Compiler.js#L104 每一个事件钩子决定了它该如何应用插件的注册

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Compiler {
  this.hooks = Object.freeze({
    initialize: new SyncHook([]),
    shouldEmit: new SyncBailHook(["compilation"]),
		done: new AsyncSeriesHook(["stats"]),
		...
  })
}

Tapable 的核心原理是发布订阅模式。基于 Tapable 使得 webpack 具有很好的扩展性,但对于调试来说比较痛苦(代码跳跃)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const {
	SyncHook,
	SyncBailHook,
	SyncWaterfallHook,
	SyncLoopHook,
	AsyncParallelHook,
	AsyncParallelBailHook,
	AsyncSeriesHook,
	AsyncSeriesBailHook,
	AsyncSeriesWaterfallHook
} = require("tapable");

主线

hook 事件注册 ==> hook 触发 ==> 生成 hook 执行代码new Function() ==> 执行

更多动态执行脚本方式 ,请参照:动态执行脚本

注册&触发

注册
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface Hook {
	tap: (name: string | Tap, fn: (context?, ...args) => Result) => void,
	tapAsync: (name: string | Tap, fn: (context?, ...args, callback: (err, result: Result) => void) => void) => void,
	tapPromise: (name: string | Tap, fn: (context?, ...args) => Promise<Result>) => void
}
触发
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface Hook {
	call: (...args) => Result,
	callAsync: (...args, callback: (err, result: Result) => void) => void,
  	promise: (...args) => Promise<Result>
}

Hook types

按时序方式分类
  • Sync: 同步,通过 myHook.tap() 调用
  • Async:异步
    • AsyncSeries: 连续调用每个异步方法,通过 myHook.tap()myHook.tapAsync()myHook.tapPromise() 调用
    • AsyncParallel:并行运行每个异步方法,调用方式同 AsyncSeries
按流程控制分类
  • 基础类型:名称中没有 Bail、Waterfall、Loop,在触发事件之后,会按照事件注册的先后顺序执行所有的事件处理函数,不关心返回值。SyncHook、AsyncParallelHook、AsyncSeriesHook
  • Waterfall:「返回结果具有流动性–瀑布」如果前一个 Hook 函数的结果 result !== undefined,则 result 会作为后一个 Hook 函数的第一个参数。-- 有点类似于 Array.prototype.reduce()SyncWaterfallHook、AsyncSeriesWaterfallHook 注意:没有 AsyncParallelWaterfallHook,并行操作无法确保返回顺序,值无法传递 类别Interface返回形式tapfn: (context?, ...args) => Resultreturn resulttapAsyncfn: (context?, ...args, callback: (err, result: Result) => void) => voidcallback(err, result)tapPromisefn: (context?, ...args) => Promiseresolve(result)
  • Bail:顺序执行 Hook,遇到第一个结果 result !== undefined 则返回,不再继续执行。-- 有点类似于Promise.race(iterable)SyncBailHook、AsyncSeriseBailHook、AsyncParallelBailHook
  • Loop:不停的循环执行 Hook,直到所有函数结果 result === undefinedSyncLoopHook、AsyncSeriseLoopHook
示例

示例1:基础示例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const { SyncHook } = require('tapable')

class Test {
  constructor () {
    this.hooks = {
      compiler: new SyncHook(['name'])
    }
  }
  tap () {
    this.hooks.compiler.tap('consumer1', (name) => {
      console.log('consumer1', name)
      return 'consumer1'
    })
    this.hooks.compiler.tap('consumer2', (name) => {
      console.log('consumer2', name)
      return 'consumer2'
    })
  }
  call () {
    this.hooks.compiler.call('ligang')
  }
}
const t = new Test()
t.tap()
t.call()
// 输出结果
// consumer1 ligang
// consumer2 ligang

SyncHook 不会处理 result 值。

示例2:SyncWaterfallHook

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const { SyncWaterfallHook } = require('tapable')

class Test {
  constructor () {
    this.hooks = {
      compiler: new SyncWaterfallHook(['name'])
    }
  }
  tap () {
    this.hooks.compiler.tap('consumer1', (name) => {
      console.log('consumer1', name)
      return 'consumer1'
    })
    this.hooks.compiler.tap('consumer2', (name) => {
      console.log('consumer2', name)
      return 'consumer2'
    })
  }
  call () {
    this.hooks.compiler.call('ligang')
  }
}
const t = new Test()
t.tap()
t.call()
// 输出结果
// consumer1 ligang
// consumer2 consumer1

return 值不是undefined,发生了传递。

示例3:SyncBailHook

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const { SyncBailHook } = require('tapable')

class Test {
  constructor () {
    this.hooks = {
      compiler: new SyncBailHook(['name'])
    }
  }
  tap () {
    this.hooks.compiler.tap('consumer1', (name) => {
      console.log('consumer1', name)
      return 'consumer1'
    })
    this.hooks.compiler.tap('consumer2', (name) => {
      console.log('consumer2', name)
      return 'consumer2'
    })
  }
  call () {
    this.hooks.compiler.call('ligang')
  }
}
const t = new Test()
t.tap()
t.call()

consumer1 返回的 result 值不是 undefined,因此后续路程被终止,consumer2 未被执行!

示例4:其他

异步方式类似,只要注意返回的 result 形式不同。

  • tapreturn result
  • tapAsynccallback(err, result)
  • tapPromiseresolve(result)

拦截 intercept

所有的 Hook 都提供了额外的拦截API

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interface HookInterceptor {
	call: (context?, ...args) => void,
	loop: (context?, ...args) => void,
	tap: (context?, tap: Tap) => void,
	register: (tap: Tap) => Tap
}
  • register: (tap: Tap) => Tap | undefined 插件用 tap* 方法注册时触发;
  • call: (...args) => void 当被call 调用时触发,并可以访问到 hooks 参数(call 之前触发);
  • tap: (tap: Tap) => void 当插件被调用触发,提供的是Tap对象,但不可修改(在call 之后,回调之前触发);
  • loop: (...args) => void loop hook 的插件被调用时触发(每个循环触发);
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const { SyncHook } = require('tapable')

class Test {
  constructor () {
    this.hooks = {
      compiler: new SyncHook(['name'])
    }
  }
  interceptor () {
    this.hooks.compiler.intercept({
      register: (tap) => {
        console.log('register!!!', tap)
        return tap
      },
      call: (args => {
        console.log('call!!!', args)
      }),
      tap: (tap => {
        console.log('tap!!!', tap)
      }) 
    }) 
  }
  tap () {
    this.hooks.compiler.tap('consumer1', (name) => {
      console.log('consumer1', name)
      return 'consumer1'
    })
    this.hooks.compiler.tap('consumer2', (name) => {
      console.log('consumer2', name)
      return 'consumer2'
    })
  }
  call () {
    this.hooks.compiler.call('ligang')
  }
}
const t = new Test()
t.interceptor()
t.tap()
t.call()
// 输出结果
// register!!! { type: 'sync', fn: [Function], name: 'consumer1' }
// register!!! { type: 'sync', fn: [Function], name: 'consumer2' }
// call!!! ligang
// tap!!! { type: 'sync', fn: [Function], name: 'consumer1' }
// consumer1 ligang
// tap!!! { type: 'sync', fn: [Function], name: 'consumer2' }
// consumer2 ligang

上下文 Context

插件和拦截器都可以往里面传一个上下文对象的参数,该对象可用于向后续插件和拦截器传递任意值。通过此,可以对拦截或后续插件做灵活控制!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const { SyncHook } = require('tapable')

class Test {
  constructor () {
    this.hooks = {
      compiler: new SyncHook(['name'])
    }
  }
  interceptor () {
    this.hooks.compiler.intercept({
      context: true,
      call: (context, args) => {
        context.params = {a: 1, b: 2}
        console.log('call!!!', args)
      }
    }) 
  }
  tap () {
    this.hooks.compiler.tap({
      name: 'consumer',
      context: true
    }, (context, name) => {
      console.log('consumer1', name, context)
      return 'consumer1'
    })
  }
  call () {
    this.hooks.compiler.call('ligang')
  }
}
const t = new Test()
t.interceptor()
t.tap()
t.call()
// 输出结果
// call!!! ligang
// consumer1 ligang { params: { a: 1, b: 2 } }

参考地址

  • https://github.com/webpack/tapable
  • https://segmentfault.com/a/1190000020146256
  • https://segmentfault.com/a/1190000017420937
  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
  • https://github.com/webpack/webpack/blob/master/lib/Compiler.js
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/07/11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
[源码解读] Webpack 插件架构深度讲解
本文大部分篇幅都 focus 在 Tapable 框架,详细枚举了 Tapable 提供的钩子及各类型钩子的特点、运行逻辑、实现原理,并进一步讨论 Tapable 框架在 webpack 的作用,进而揭示 webpack 插件架构的核心逻辑。
Tecvan
2021/12/09
1.7K0
[源码解读] Webpack 插件架构深度讲解
webpack原理(3):Tapable源码分析及钩子函数作用分析
webpack本质上是一种事件流的机制,它的工作流程就是将各个插件串联起来,而实现这一切的核心就是Tapable,webpack中最核心的负责编译的Compiler和负责创建bundles的Compilation都是Tapable的实例。
周陆军博客
2023/03/18
6740
【Webpack】1256- 硬核解析 Webpack 事件流核心!
Tapable 是 Webpack 整个生命周期及其插件机制的事件流实现,它提供了多种形式的发布订阅模式的 API,我们可以利用它来注册自定义事件,并在不同的时机去触发。
pingan8787
2022/04/14
1.8K0
【Webpack】1256- 硬核解析 Webpack 事件流核心!
webpack-插件机制杂记 系列文章前言Tapablecompilecompilation编写一个插件compiler和compilation一些比较重要的事件钩子总结引用
webpack本身并不难,他所完成的各种复杂炫酷的功能都依赖于他的插件机制。或许我们在日常的开发需求中并不需要自己动手写一个插件,然而,了解其中的机制也是一种学习的方向,当插件出现问题时,我们也能够自己来定位。
菜的黑人牙膏
2019/03/15
1.3K0
聊聊 Webpack 插件系统的关键实现 Tapable
蛋先生:首先,每个人的一天都有这么几个阶段:早上,中午,下午,晚上。用 Tapable 的方式描述是以下这个样子:
蛋先生DX
2022/03/27
5970
聊聊 Webpack 插件系统的关键实现 Tapable
Webpack 详解
webpack是现代前端开发中最火的模块打包工具,只需要通过简单的配置,便可以完成模块的加载和打包。那它是怎么做到通过对一些插件的配置,便可以轻松实现对代码的构建呢?
前端教程
2018/07/27
5900
Webpack 详解
webpack编写一个插件
插件向第三方开发者提供了 webpack 引擎中完整的能力。使用阶段式的构建回调,开发者可以引入它们自己的行为到 webpack 构建流程中。创建插件比创建 loader 更加高级,因为你将需要理解一些 webpack 底层的内部特性来实现相应的钩子,所以做好阅读一些源码的准备!
前端迷
2019/06/19
1.4K0
Webpack——从基础使用到手动实现(万字长文)
这个其实不用多说了,如今的前端项目哪里还有用不到打包工具的呢,而webpack又作为打包工具中的王者我们又有什么理由不去搞明白它呢。
coder_koala
2020/07/15
1.1K0
Webpack——从基础使用到手动实现(万字长文)
前端推荐!玩转Webpack共需几步?
导语 | 本文主要介绍webpack的打包流程,及其插件系统Tabable,并手写了一下简易打包器。通过这篇文章读者可以了解webpack的具体实现过程,并且自己也可以理解其打包原理,有利于更好的使用这些工具。 一、开始 Webpack打包原理是从入口文件开始分析AST,递归收集依赖,然后生成最终的code。Webpack的插件是贯穿始终的,其插件系统借助了Tapable,Tapable也是Webpack团队开发的,其本质是一种发布订阅模式。 深入理解插件对于深入理解Webpack非常重要。想一下,任何复杂
腾讯云开发者
2022/02/10
4860
Tapable,看这一篇就够了
Webpack 在前端工程化中可谓是大名鼎鼎,在Webpack编译过程中存在两个核心对象。
19组清风
2022/02/28
1.7K0
Tapable,看这一篇就够了
揭秘webpack插件工作流程和原理
通过插件我们可以扩展webpack,在合适的时机通过Webpack提供的 API 改变输出结果,使webpack可以执行更广泛的任务,拥有更强的构建能力。 本文将尝试探索 webpack 插件的工作流程,进而去揭秘它的工作原理。同时需要你对webpack底层和构建流程的一些东西有一定的了解。
前端迷
2020/05/26
1.9K0
Webpack 基石 tapable 揭秘
Webpack 基于 tapable 构建了其复杂庞大的流程管理系统,基于 tapable 的架构不仅解耦了流程节点和流程的具体实现,还保证了 Webpack 强大的扩展能力;学习掌握tapable,有助于我们深入理解 Webpack。
2020labs小助手
2021/03/10
8890
手把手带你入门Webpack Plugin
? 这是第 101 篇不掺水的原创,想要了解更多,请戳上方蓝色字体:政采云前端团队 关注我们吧~ 本文首发于政采云前端团队博客:手把手带你入门Webpack Plugin https://www.z
政采云前端团队
2021/06/15
6810
手把手带你入门Webpack Plugin
tapable钩子函数介绍
webpack的的实现是基于tapable,来看一下tabable的各个钩子函数的使用。
挥刀北上
2021/03/03
6830
tapable钩子函数介绍
tapable(webpack核心模块)使用手册
前不久写了一篇webpack基本原理和AST用法的文章[1],本来想接着写webpack plugin的原理的,但是发现webpack plugin高度依赖tapable[2]这个库,不清楚tapable而直接去看webpack plugin始终有点雾里看花的意思。所以就先去看了下tapable的文档和源码,发现这个库非常有意思,是增强版的发布订阅模式。发布订阅模式在源码世界实在是太常见了,我们已经在多个库源码里面见过了:
公众号@魔术师卡颂
2021/05/08
7030
tapable(webpack核心模块)使用手册
2. webpack构建的基石: tapable@1.1.3源码分析
通过一个demo带你深入进入webpack@4.46.0源码的世界,分析构建原理,专栏地址,共有十篇。
tinyant
2022/11/16
4600
2. webpack构建的基石: tapable@1.1.3源码分析
webpack插件怎么手写
webpack插件没什么好说的,用过的都知道怎么配置,只是不知道内部怎么执行。今天学一学插件的一些机制,手写一个插件并不难。
wade
2020/09/14
9410
webpack插件怎么手写
显微镜下的webpack4:灵魂tapable,终于搞懂钩子系列!
大家在看webpack源码的时候,有没有感觉像再看天书,似乎没有办法一个文件比如webpack.js从头看到尾。感觉webpack的跳跃性很强,完全不知道程序在运行的时候,发生了什么。完全不清楚这个事件是什么时候发生的,比如loader是什么时候执行的,plugin又是什么时候出现的。webpack的程序错综复杂,完全迷失在程序之中。这究竟是为什么呢?其实很简单!因为webpack的灵魂Tapable!这个机制使得webpack异常的灵活,它有一句经典的话——Everything is a plugin!。由此可见webpack是靠插件堆积起来的。而实现这个插件机制的就是Tabable!
小美娜娜
2019/04/04
8840
Webpack Plugin知识分享
原创不易,未经作者允许禁止转载!! 认识Plugin Loader是用于特定的模块类型进行转换; Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等; 常用Plugin CleanWebpackPlugin 每次修改了一些配置,重新打包时,都需要手动删除dist文件夹: 我们可以借助于一个插件来帮助我们完成,这个插件就是CleanWebpackPlugin; 安装: npm install clean-webpack-plugin --save 配置: const { Clea
前端LeBron
2021/12/08
4260
Webpack中的plugin插件机制
上一篇 「webpack 核心特性」loader 说到 webpack 的 loader 机制,本文主要聊一聊另外一个核心特性:插件(plugin)。
gogo2027
2022/10/21
7840
推荐阅读
相关推荐
[源码解读] Webpack 插件架构深度讲解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验