webpack插件demo编写
场景:项目仓库是小部件编码管理,需要将生成的dist内单个小部件文件夹手动打包成zip,再上传至项目平台。
因为每次打包完之后都要手动压缩,略微有些繁琐(每次手动压缩十几个文件,一天N次,要疯,果然偷懒是第一生产力),正好也要试一下怎么写webpack插件,就实现了一个很小的插件demo。
1. webpack怎么写插件
webpack官网原文:
插件是 webpack 生态系统的重要组成部分,为社区用户提供了一种强大方式来直接触及 webpack 的编译过程(compilation process)。插件能够 钩入(hook) 到在每个编译(compilation)中触发的所有关键事件。在编译的每一步,插件都具备完全访问 compiler 对象的能力,如果情况合适,还可以访问当前 compilation 对象。
我的理解就是插件可以在webpack编译的某些步骤执行插件中的方法(类似vue中不同生命周期执行不同的方法)
1.1 hooks+触发时机
webpack拥有的钩子:compiler
、compilation
各自抛出的生命周期函数
Tapable:webpack的核心工具,暴露tap
(同步), tapAsync
(异步) 和 tapPromise
(延迟的异步)方法
eq.
// 钩子.hooks.钩子的生命周期函数.触发机制('pluginName', 函数抛出的参数 => { 执行代码 })
compiler.hooks.someHook.tap('', params => {...})
1.2 怎么调用插件
了解了执行时机之后,需要知道怎么让我们的插件和webpack发生关联,并拿到compiler
钩子,才能使用钩子对象。
webpack会调用插件的apply属性,类似vue会调用install属性一样
webpack在配置时是new的实例,并且可以传参
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
}
综上:一个插件的结构就出来了
function BuildAddZip (options) {
this.options = options
}
BuildAddZip.prototype.apply = function (compiler) {
compiler.hooks.done.tap('BuildAddZip', stats => {
...
}
}
2. 小demo实现步骤
2.1 需求
打包后dist内的文件夹自动生成zip包
2.2 代码分析
- 插件在哪个钩子函数中执行:dist是在打包完成之后,所以
compiler
钩子中的done
(编译完成) 可以拿到最新的打包资源 - 如何获取最新的打包文件:done抛出的参数stats中可以获取
stats.toJson().assets
- 如何新增zip包:
adm-zip
(百度的包,这个包据说解压有点问题,这里只用到压缩)
2.3 代码实现
const AdmZip = require('adm-zip')
function BuildAddZip (options) {
// 获取参数,这边没有使用到,假装获取下
this.options = options
}
BuildAddZip.prototype.apply = function (compiler) {
// 获取打包输出文件路径
const output = compiler.options.output.path
compiler.hooks.done.tap('BuildAddZip', stats => {
// ['draw/index.js', 'draw/asstes/img/a.png', 'home/index.js', 'home/index.html'...]
// 获取打包文件中的文件路径, 并获取第一层文件名
// ['draw', 'draw', 'home', 'home']
const allFilesName = states.toJson().assets.map(asset => asset.name.split('/')[0])
// 去重,['draw', 'home']
const allFiles = [...new Set(allFilesName)]
allFiles && allFiles.length && allFiles.forEach(filename => {
// 打包
const zip = new AdmZip()
// 这一行是为了层级,比如对整个draw打包,没有则是对draw内部文件打包,包了一层draw
zip.addLocalFolder(`${output}/${filename}`)
zip.writeZip(`${output}/${filename}.zip`)
})
})
}