【笔记】Tapable源码解析图以及webpack怎样实现一个插件plugin
Tapable源码解析图,如图所示:
一个webpack plugin由一下几个步骤组成:
- 一个JavaScript类函数。
- 在函数原型 (prototype)中定义一个注入
compiler
对象的apply
方法。 apply
函数中通过compiler插入指定的事件钩子,在钩子回调中拿到compilation对象- 使用compilation操纵修改webapack内部实例数据。
- 异步插件,数据处理完后使用callback回调
开发webpack插件还需要了解其中两个核心的对象引用Compiler 和 Compilation,这里需要理解清楚他们的含义。
compiler
对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立,并配置好所有可操作的设置,包括 options,loader 和 plugin。当在 webpack 环境中应用一个插件时,插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。compilation
对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时,每当检测到一个文件变化,就会创建一个新的 compilation,从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用。
实现一个如下需求的插件,针对某个打包生成的JS,对其内容的头部添加一个eslint语法检测的忽略说明,那样eslint就不会检测当前打包的js,如下代码:
class ignoreEslintPlugin { constructor(options) { this.options = options; } apply(compiler) { compiler.hooks.emit.tap('ignoreEslintPlugin', (compilation) => { var topInfo = '/* eslint-disable */\n'; var content = topInfo + compilation.assets[this.options.filename].source(); // console.info(chalk.green(content)) compilation.assets[this.options.filename] = { source: () => content, size: () => content.length } }) } }
调用方式:
new ignoreEslintPlugin({filename: 'common.js'})
我们看下上面代码中的这个结构:
compiler.hooks.emit.tap('ignoreEslintPlugin', (compilation) => {
})
这一步主要是使用核心对象compiler的emit钩子,通过.tap方法注册到webpack中,在输出构建产物到dist目录之前执行。 在这里列出compiler对象的生命周期钩子
-> beforeRun 清除缓存
-> run 注册缓存数据钩子
-> beforeCompile
-> compile 开始编译
-> make 从入口分析依赖以及间接依赖模块,创建模块对象
-> buildModule 模块构建
-> normalModuleFactory 构建
-> seal 构建结果封装, 不可再更改
-> afterCompile 完成构建,缓存数据
-> emit 输出到dist目录
参考地址:怎样编写一个简单的webpack插件