# 编写一个plugin
# 插件结构
插件是伴随webpack构建的初始化到最后文件生成的整个生命周期,插件的目的是在于解决loader无法实现的其他事情。
另外,插件没有像loader那样的独立运行环境,所以插件的开发只能在webpack里面运行。
插件的基本结构:
// 提供一个class类
class Plugin{
// 接受插件传递的参数
constructor(options){
...
}
// 插件上提供apply方法,webpack提供compiler对象
apply(compiler){
// 指定一个挂载到 webpack 自身的事件钩子
// compiler提供hooks钩子方法,作用于构建流程中
compiler.hooks.done.tap('Plugin',
// 功能完成后调用 webpack 提供的回调
// 插件处理逻辑
()=>{
...
})
}
}
module.exports = Plugin
至此,了解到webpack插件的组成部分:
- 提供一个class类函数
- 插件上提供apply方法,接受compiler参数。compiler 对象代表了完整的 webpack 环境配置。 这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。
- 指定一个挂载到 webpack 自身的事件钩子,使用 compiler 对象时,可以绑定提供了编译 compilation 引用的回调函数, 一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。
- 功能完成后调用 webpack 提供的回调
# 插件事件处理
# 插件中获取传递的参数
通过插件的构造函数进行获取。
class Plugin{
// 接受插件传递的参数
constructor(options){
this.options = options
}
}
# 异步插件
有一些编译插件中的步骤是异步的,这样就需要额外传入一个 callback 回调函数,并且在插件运行结束时,必须调用这个回调函数。
# 插件的错误处理
- 参数校验阶段可以直接throw的方式将错误抛出
throw new Error('error options')
- 如果已经进入到hooks钩子阶段,可以通过compilation对象的warnings和errors接收
compilation.warnings.push('warnings')
compilation.errors.push('error')
# 对资源文件处理:通过Compilation进行文件写入
对于一些插件,本身需要产生一些静态资源。比如生成zip包,之前源码浏览时候有了解到,文件生成阶段
是在emit阶段,则我们可以监听compiler的emit钩子阶段,获取到compilation对象,并将最终的内容
设置到compilation.assets对象上输出到磁盘目录。
另外,文件的写入,一般需要借助webpack-sources
const { RawSource } = require('webpack-sources')
module.exports = class Plugin {
constructor(options){
this.options = options
}
apply(compiler){
compiler.plugin("emit", function(compilation, callback) {
compilation.assets['name'] = new RawSource('demo')
callback()
});
}
}
# 插件扩展:编写插件的插件
插件自身也可以通过暴露hooks的方式进行自身扩展,例如html-webpack-plugin就暴露了 html-webpack-plugin-after-chunk(Sync)、html-webpack-plugin-before-html-generation(Async)等钩子。
# 插件实战
编写一个压缩构建资源为zip包的插件。其中,可实现根据参数生成zip包的文件名称。
实现思路:
- 使用jszip实现zip包生成。
- 使用compiler对象的emit钩子,compiler.hooks.emit对tapAsync方法实现监听,在emit 生成文件阶段,将zip包设置到compilation.assets对象上。
//zip-webpack-plugin
const path = require('path')
const JSZip = require('jszip')
const { RawSource } = require('webpack-sources')
const zip = new JSZip()
class MinZipWebpackPlugin{
constructor(options){
this.options = options
}
apply(compiler){
compiler.hooks.emit.tapAsync('MinZipWebpackPlugin', (compilation,callback)=>{
// zip.folder,创建目录名称
const folder = zip.folder(this.options.filename);
// 遍历compilation.assets对象
for(let filename in compilation.assets){
// 获取source
const source = compilation.assets([filename]).source()
// 将source添加到folder中
folder.file(filename, source)
}
// 将内容生成zip
zip.generateAsync({
type: 'nodebuffer'
}).then((content)=>{
// 获取output(绝对路径)
const outputPath = path.join(
compilation.options.output.path,
this.options.filename + '.zip'
)
const outputRelativePath = path.relative(
compilation.options.output.path,
outputPath
)
// 将内容挂载到compilation.assets上,并将buffer转换为source
compilation.assets[outputRelativePath] = new RawSource(content)
callback()
})
})
}
}
module.exports = MinZipWebpackPlugin
线上代码:https://github.com/PCAaron/min-zip-webpack-plugin,对你有帮助的话就给个star吧~
# 推荐阅读
编写一个插件:https://www.webpackjs.com/contribute/writing-a-plugin/