plugin是什么
plugin 本质上是一个具有 apply
方法 javascript 对象,而他的这个 apply
方法会被 webpack 在compiler 阶段调用,并且在整个编译生命周期都可以访问 compiler 对象
Plugin工作原理
webpack的工作流程就像一个流水线,经过一系列处理流程后才能将源文件转换为目标文件;每个流程是单一职责的,流程相互之间是同步机制;插件就是插入到流水线的一个功能,在特定的时机对生产线上的资源进行处理,webpack通过Tapable来组织这条流水线,webpack在运行过程中会广播事件,插件需要监听所有与他有关的事,这样才能加入到流水线中去改变生产线的运作
找到钩子事件并挂载上自己的任务就是注册事件,这样当webpack构建的时候,插件注册的事件执行。
什么是钩子
钩子的本质是事件,为了方便我们介入和控制编译的过程,webpack把编译过程中触发的各类事件,封装成事件接口暴露出来,这些接口被称为hooks
钩子,开发插件也离不开钩子
Tapable
Tapable为webpack提供了统一的插件接口定义类型,是webpack的核心功能库,webpack库中目前有十种类型hooks
他们是:
- SyncHook
- SyncBailHook
- SyncWaterfallHook
- SyncLoopHook
- AsyncParallelHook
- AsyncParallelBailHook
- AsyncSeriesHook
- AsyncSeriesBailHook
- AsyncSeriesWaterfallHook
Tapable还暴露了三个方法给插件,用于注入不同类型的自定义构建行为
tap
:可以注册同步钩子和异步钩子tapAsync
:回调方法注册异步钩子tapPromise
:Promise方式注册异步钩子
Plugin构建对象
Complier
Complier保存着webpack环境配置,每次启动webpack构建时都是独一无二的,只会创建一次,这个对象在webpack首次启动时构建,我们可以通过complier对象访问到webpack的主环境配置,如loader,plugin...配置信息
它的主要属性有:
- complier.options:启动时webpack的所有配置文件
- complier.inputFileSystem 和 complier.outputFileSystem 可以进行文件操作相当于Node.js的fs
- complier.hooks:可以注册Tapable的不同种类Hook,从而在complier生命周期中植入不同的逻辑。
具体内容可以去官网阅读
Compilation
Compilation表示一次资源构建,Compilation实例能访问到所有模块和他们的依赖。 一个compilation对象会对构建依赖途中的所有模块,进行编译,在编译阶段,模块会加载(load),封存(seal),优化(optimize),分块(chunk),哈希(hash)和重建(restore)
主要的属性
- compilation.modules可以访问所有模块,打包每一个文件和每一个模块。
- compilation.chunks由多个modules组成的一个代码块。
- compilation.assets可以访问到打包生成的所有文件结果
- compilation.hooks可以注册tapable的不同类型Hook,用于compilation编译模块阶段进行逻辑添加及修改
在了解了执行流程之后
自定义插件
- 新建一个测试插件 在 src/plugin新建一个文件
class TestP {
constructor() {
console.log('test TestPlugin')
}
apply(complier) {
console.log('test TestPluginApply')
}
}
module.exports = TestP
在webpack配置中修改plugins配置
const TesPlugin = require('./src/plugin/test')
module.exports = {
......
configureWebpack: (config) => {
.....
config.plugins.push(new TesPlugin())
....
})
}
这里的执行步骤是
- webpack加载webpack.config.js的所有配置此时会new TestP() 执行插件的构造器
- webpack创建complier对象
- 遍历所有plugins中的插件,调用插件的apply,再执行剩下的编译流程
- 执行剩下的编译流程触发各个hooks
通过控制台输出的时间顺序可以看出
chainWebpack和configureWebpack的区别
chainWebpack 配置项允许我们更细粒度的控制 webpack 的内部配置,可以让我们能够使用链式操作来修改配置。使用webpack-chain
维护
eg:
chainWebpack: config => {
// 移除 prefetch 插件
config.plugins.delete('prefetch')
config.module
.rule('file')
.test(/\.(zip|xls|pdf|doc|docx)(\?.*)?$/)
.use('file-loader')
.loader('url-loader')
.options({ limit: 1, name: 'file/[name].[ext]' })
.end()
configureWebpack 更倾向于整体替换和修改
configureWebpack 可以直接是一个对象,也可以是一个函数,如果是对象它会直接使用 webpack-merge
对其进行合并处理,如果是函数,你可以直接使用其 config 参数来修改 webpack 中的配置,或者返回一个对象来进行 merge 处理
eg:
// 开发环境不需要gzip
if (process.env.NODE_ENV === 'production') {
config.plugins.push(
new CompressionWebpackPlugin({
// 正在匹配需要压缩的文件后缀
test: /\.(js|css|svg|woff|ttf|json|html)$/,
// 大于10kb的会压缩
threshold: 10240
// 其余配置查看compression-webpack-plugin
// deleteOriginalAssets: true // 删除原文件
// minRatio:0.8, // 只有压缩率小于这个值的资源才会被处理
// filename: '[path].gz[query]',
})
)
}
},