plugin是webpack生态的重要组成,它为用户提供了一种可以直接访问到webpack编译过程的方式。它可以访问到编译过程触发的所有关键事件。

1. 基本概念

1. 如何实现一个插件

1. plugin实际是一个类(构造函数),通过在plugins配置中实例化进行调用。

// webpack.config.js
var MyExampleWebpackPlugin = require('my-example-webpack-plugin'); module.exports = {
// ... 这里是其他配置 ...
plugins: [new MyExampleWebpackPlugin({ options: xxx })]
};

2. 它在原型对象上指定了一个apply方法,入参是compiler对象

3. 指定一个事件钩子,并调用内部提供的API

4. 完成操作后,调用webpack 提供的callback方法

// 一个 JavaScript class
class MyExampleWebpackPlugin {
// 将 `apply` 定义为其原型方法,此方法以 compiler 作为参数
apply(compiler) {
// 指定要附加到的事件钩子函数
compiler.hooks.emit.tapAsync('MyExampleWebpackPlugin',
(compilation, callback) => {// 使用 webpack 提供的 plugin API 操作构建结果
compilation.addModule(/* ... */);
callback();
}
);
}
}

2. 实现插件的背景知识

由上面的步骤可知,插件功能的实现主要依赖于compiler和complation对象,而两者都是继承自Tapable对象。它暴露三种注册监听的方法Tapable对象主要是9种钩子:

const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncParallelHook,
AsyncParallelBailHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook
} = require("tapable");

其中同步四种,异步并行两种,异步串行3种。

同步钩子进行同步操作;异步钩子中进行异步操作。

compiler和compilation中的钩子都是自称自这9种钩子。钩子的工作机制类似于浏览器的事件监听。

1)生成的钩子可以注册监听事件,其中同步钩子通过tap方法监听,异步钩子通过tapAsync(+回调函数)和tapPromise(+返回promise)进行监听。

2)还可以进行拦截,通过intercept方法。

3)对于监听事件的触发,同步钩子通过call方法; 异步钩子通过callAsync方法和promise

示例1: -SyncHook

监听事件都是按照注册的顺序依次执行

const { SyncHook } = require('tapable');

const hook = new SyncHook(['name', 'age']);
hook.tap('任意字符1', (name, age) => {
console.log(1);
return undefined;
})
hook.tap('任意字符2', (name, age) => {
console.log(2);
return true;
})
hook.tap('任意字符3', (name, age) => {
console.log(3);
})
hook.call('lyra', 18); //传入的参数必须和初始化实例时传入的参数个数相同
// 执行顺序如下
1
2
3

示例2: -SyncBailHook

只要返回非undefined值,终止监听事件的调用

示例3:-SyncWaterfallHook

监听事件如果返回非undefined值,作为下个监听事件的第一个参数;如果返回undefined,返回之前的监听事件中最近的非undefined值

const { SyncWaterfallHook } = require('tapable');

const hook = new SyncWaterfallHook(['name', 'age']);
hook.tap('任意字符1', (name, age) => {
console.log('1-->',name,age);
return 19;
})
hook.tap('任意字符2', (name, age) => {
console.log('2-->',name,age);
return undefined;
})
hook.tap('任意字符3', (name, age) => {
console.log('3-->',name,age);
return 21;
})
hook.call('lyra', 18); //传入的参数必须和初始化实例时传入的参数个数相同
// 执行顺序如下
1--> lyra 18
2--> 19 18
3--> 19 18

示例4: -SyncLoopHook

只要监听事件返回的是非undefined值,则回到该钩子的第一个监听事件从头开始执行

示例5: -AsyncSeriesHook(complier.hooks.emit)

串行异步会等前一个事件结束再执行第二个监听事件

// 串行异步,则耗时需要3s
const { AsyncSeriesHook } = require('tapable'); const hook = new AsyncSeriesHook(['name', 'age']);
hook.tapPromise('任意字符1', (name, age) => {
// 因为使用的是tapPromise监听,必须返回一个promise
console.time(1)
return new Promise(function(resolve, reject){
setTimeout(function() {
resolve(1);
}, 1000)
})
})
hook.tapPromise('任意字符2', (name, age) => {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve(1);
console.timeEnd(1)
}, 2000)
});
})
// 通过promise方法触发监听
hook.promise('lyra', 18).then(() => {
// TODO
});

示例6: - AsyncParallelHook(complier.hooks.make)

并行异步,所有监听函数同时执行

// 并行异步,耗时2秒,其实就是所有监听事件中耗时最长的事件
const { AsyncParallelHook } = require('tapable'); const hook = new AsyncParallelHook(['name', 'age']);
hook.tapAsync('任意字符1', (name, age, callback) => {
// 通过tapAsync方法注册监听;通过callback方法完成监听
console.time(1)
setTimeout(() => {
callback();
},1000)
})
hook.tapAsync('任意字符2', (name, age, callback) => {
setTimeout(() => {
callback();
console.timeEnd(1)
}, 2000)
})
// 通过callAsync方法触发监听;且必须有回调函数
hook.callAsync('lyra', 18, function(e) {
//该函数必须存在
});

2. 自定义创建插件

1. 打包zip插件

const JsZip = require('jszip');

class ZipPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
// emit是一个异步串行钩子
compiler.hooks.emit.tapPromise('1', (compilation) => {
const assets = compilation.assets;
const zip = new JsZip();
for(let filename in assets) {
zip.file(filename, assets[filename].source())
}
// nodebuffer是node环境中的二进制形式;blob是浏览器环境
return zip.generateAsync({type: 'nodebuffer'}).then((content) =>{
console.log(this.options.name);
assets[this.options.name] = {
source() {return content},
size() {return content.length} //可以省略
}
return new Promise((resolve, reject) => {
resolve(compilation)
})
})
})
}
} module.exports = ZipPlugin;

在webpack.config.js中使用

const ZipPlugin = require('./plugins/ZipPlugin');

module.exports = {
plugins: [
new ZipPlugin({
name: 'my.zip'
})
]
}

webpack的plugin原理的更多相关文章

  1. webpack 的编译原理

    自从接触了react,vue 这两个框架,都会用到webpack这个打包工具.面试的时候,经常被问到知道webpack的编译原理吗? 可以简单的介绍一下.每每这个时候都被问的哑口无言,平时用的时候挺顺 ...

  2. 如何编写一个WebPack的插件原理及实践

    _ 阅读目录 一:webpack插件的基本原理 二:理解 Compiler对象 和 Compilation 对象 三:插件中常用的API 四:编写插件实战 回到顶部 一:webpack插件的基本原理 ...

  3. 轻松理解webpack热更新原理

    一.前言 - webpack热更新 Hot Module Replacement,简称HMR,无需完全刷新整个页面的同时,更新模块.HMR的好处,在日常开发工作中体会颇深:节省宝贵的开发时间.提升开发 ...

  4. MyBatis源码分析(2)—— Plugin原理

    @(MyBatis)[Plugin] MyBatis源码分析--Plugin原理 Plugin原理 Plugin的实现采用了Java的动态代理,应用了责任链设计模式 InterceptorChain ...

  5. vim plugin 原理

    vim 个性化设置与功能扩展均通过 script 来实现,这种 script 又叫 plugin.plugin 是 vim 的核心与精髓. 最常用的配置文件 vimrc,也是一种 plugin.换句话 ...

  6. [转] webpack之plugin内部运行机制

    简介 webpack作为当前最为流行的模块打包工具,几乎所有的主流前端开发框架(React.Vue等)都会将其作为默认的模块加载和打包工具.通过简单的配置项,使用各种相关的loader和plugin, ...

  7. Webpack机制、原理简单小结

    一.webpack的构成 entry  代表项目的入口 module  开发中,每一个文件可以看作一个module chunk  代码块 loader  模块转化器 plugin  扩展插件,自定义w ...

  8. webpack之 plugin(插件)

    plugin plugin是插件的意思,通常用来对某个现有的架构就行拓展 webpack中的插件,就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等 loader和plugin区别 lo ...

  9. webpack热替换原理

    前期准备: const path = require('path'); const HtmlWebpackPlugin= require('html-webpack-plugin'); const C ...

随机推荐

  1. C++分治策略实现快速排序

    问题描述: 给定一个未知顺序的n个元素组成的数组,现要利用快速排序算法对这n个元素进行非递减排序. 细节须知: (1)代码实现了利用递归对数组进行快速排序,其中limit为从已有的随机数文件中输入的所 ...

  2. 选择类排序 (简单选择排序,堆排序)— c语言实现

    选择类排序包括: (1)  简单选择排序 (2)树形选择排序 (3)堆排序 简单选择排序: [算法思想]:在第 i 趟简单选择排序中,从第 i 个记录开始,通过 n - i 次关键字比较,从 n - ...

  3. STM8L052C6T6液晶LCD驱动过程

    首先还是必要的说明,鄙人经验有限,如发现问题还请不吝赐教,反馈到邮箱mr.li.ming@qq.com 使用的液晶屏 单片机引脚 液晶的COM1-COM4分别对应单片机LCD_COM0-LCD_COM ...

  4. [译] QUIC Wire Layout Specification - Introduction & Overview | QUIC协议标准中文翻译(1) 简介和概述

    本文同步发布于: https://www.pengrl.com/p/33330/ ,转载请注明出处,谢谢. 目录 Introduction | 简介 Conventions and Definitio ...

  5. kibana内存设置

    kibana是一个基于NodeJS的单页web应用.而NodeJS则是基于Chrome V8引擎的.V8引擎对于内存的使用是有限制的,默认情况下,64位系统下约为1.4GB,32位系统下约为0.7GB ...

  6. ArcGIS Engine开发鹰眼图的功能(基础篇)

    鹰眼是用于调节全视域范围内主地图显示范围情况的副地图.它体现了地图整体与详细局部的关系. 用户可以通过鼠标单击或者画框等动作实现鹰眼与主地图的交互情况. 鹰眼功能的原理是通过主地图窗口的地图控件和鹰眼 ...

  7. ADO.net(内置类区别)随记

    Ado.net使用流程 SqlConnection->open->SqlCommand(sqlstring,conn)->(ExcuteNonQuery \ExecuteScalar ...

  8. python Mock 示例

    在Python3.x中,mock已经被集成到了unittest单元测试框架中,所以,可以直接使用. 可能你和我初次接触这个概念的时候会有这样的疑问:把要测的东西都模拟掉了还测试什么呢? 但在,实际生产 ...

  9. Java自学-操作符 位操作符

    Java的位操作符 位操作符 在实际工作中使用并不常见. 示例 1 : 一个整数的二进制表达 位操作都是对二进制而言的,但是我们平常使用的都是十进制比如5. 而5的二进制是101. 所以在开始学习之前 ...

  10. iptables限制访问

    iptables限制访问 常用命令 # 查看规则 iptables -L INPUT --line-numbers # 开放指定的端口 iptables -A INPUT -p tcp --dport ...