2.Plugin 插件
const { SyncHook, SyncBailHook, SyncWaterfallHook, SyncLoopHook, AsyncParalleHook, AsyncParalleBailHook, AsyncSeriesHook, AsyncSeriesBailHook, AsyncSeriesLoopHook, AsyncSeriesWaterfallHook } = require("tapable");tapable提供了上述十大Hook类以及三种实例方法:tap、tapAsync和tapPromise,为Webpack整个工作创建了观察 -> 触发插件的工作流。
compiler = new Compiler(options.context); compiler.options = options; if (options.plugins && Array.isArray(options.plugins)) { for (const plugin of options.plugins) { if (typeof plugin === "function") { plugin.call(compiler, compiler); } else { plugin.apply(compiler); } } }可以看到,如果在webpack.config.js中配置了plugins并且他是个数组,webpack会依次去执行每一个plugin,并且把compiler传给每一个plugin。
class Compiler extends Tapable {} class Compilation extends Tapable {}其实就是继承于Tapable的子类,那么Tapable的观察者模式就可以对上了,Compiler基于观察者模式,设计出了一系列的生命周期钩子函数。基于tapable的支持,Webpack插件系统中提供了大量的生命周期钩子函数,让每一个插件都可以在需要的时候去执行。
class HelloPlugin { constructor(options) { console.log(options); } apply(compiler) { compiler.hooks.done.tap('HelloPlugin', () => { console.log('HelloPlugin'); } } }并在webpack.config.js中定义:
const HelloPlugin = require('./HelloPlugin.js'); module.exports = { ... plugins: [ new HelloPlugin(), ], ... }执行npx webpack,可以看到打印结果。
const fs = require('fs'); class CopyPlugin { constructor(options) { this.from = options.from; this.to = options.to; } apply(compiler) { const { from, to } = this; const isDir = fs.statSync(from).isFile() ? false : true; compiler.hooks.done.tap('CopyPlugin', () => { fs.cp(from, to, { recursive: isDir }, (err) => { if (err) { throw err; } }) }) } } module.exports = CopyPlugin;在webpack.config.js中配置:
const path = require('path'); const CopyPlugin = require('./copyPlugin'); module.exports = { ... plugins: [ new CopyPlugin({ from: path.resolve(__dirname, 'static'), to: path.resolve(__dirname, 'dist', 'static') }) ], ... }这里我们选择将static文件夹复制到dist目录中,使用方式和原来的一样,执行npx webpack可以看到:
const fs = require('fs'); const path = require('path') class CleanPlugin { apply(compiler) { compiler.hooks.make.tap('CleanPlugin', () => { function clearDir(dirPath) { let files = [] if (fs.existsSync(dirPath)) { files = fs.readdirSync(dirPath) files.forEach(f => { const absPath = dirPath + '/' + f; if (fs.statSync(absPath).isDirectory()) { clearDir(absPath); } else { fs.unlinkSync(absPath); } }) } } clearDir(path.resolve(__dirname, 'dist')) }) } } module.exports = CleanPlugin;在webpack.config.js中配置:
const path = require('path'); const CleanPlugin = require('./CleanPlugin'); module.exports = { ... plugins: [ new CleanPlugin(), ], ... }运行npx webpack,会发现,在打包中,目录被清除了,最后又出现文件了。