跳到主要内容

钩子

2024年04月06日
柏拉文
越努力,越幸运

一、认识


1.1 compiler

Compiler 全局构建管理器,Webpack 启动后会首先创建 compiler 对象,负责管理配置信息、LoaderPlugin 等。从启动构建到结束。compiler 大致上会触发如下钩子:

Preview

1.2 compilation

Compilation 单次构建过程的管理器,负责遍历模块,执行编译操作;当 watch = true 时,每次文件变更触发重新编译,都会创建一个新的 compilation 对象。compilation 生命周期中主要触发如下钩子:

Preview

二、compiler.hooks.run


class RunPlugin{
apply(compiler){
compiler.hooks.run.tap('Plugin Run', (compiler)=>{
console.log("compiler")
});
}
}

二、compiler.hooks.emit


class EmitPlugin{
apply(compiler){
compiler.hooks.emit.tap('Plugin Emit', (compilation)=>{
console.log("compilation")
});
}
}

三、compiler.hooks.done


class DonePlugin{
apply(compiler){
compiler.hooks.done.tap('Plugin Don', (stats)=>{
console.log("stats")
});
}
}

四、compiler.hooks.make


compiler.hooks.make 是在正式开始构建时触发。参数为当前编译的 compilation 对象。

class MakePlugin{
apply(compiler){
compiler.hooks.done.tap('Plugin Make', (compilation)=>{
console.log("compilation",compilation)
});
}
}

五、compiler.hooks.compile


class CompilePlugin{
apply(compiler){
compiler.hooks.compiler.tap('Plugin Compiler', (params)=>{
console.log("params",params)
});
}
}

六、compiler.hooks.compilation


compiler.hooks.compilation 是在 Webpack 刚启动完,创建出 compilation 对象后触发。参数为当前编译的 compilation 对象。

class CompilationPlugin{
apply(compiler){
compiler.hooks.compilation.tap('Plugin Compilation', (compilation,params)=>{
console.log("params",params)
});
}
}

七、compiler.hooks.normalModuleFactory


Compiler 使用 NormalModuleFactory 模块生成各类模块。从入口点开始,此模块会分解每个请求,解析文件内容以查找进一步的请求,然后通过分解所有请求以及解析新的文件来爬取全部文件。在最后阶段,每个依赖项都会成为一个模块实例。

我们可以通过 NormalModuleFactory Hook 来注入 Plugin 逻辑从而控制 Webpack 中对于默认模块引用时的处理,比如 ESMCJS 等模块引入前后时注入对应逻辑。

7.1 语法

class NormalModuleFactoryPlugin{
apply(compiler){
compiler.hooks.normalModuleFactory.tap('Plugin NormalModuleFactory', (normalModuleFactory)=>{
console.log("normalModuleFactory",normalModuleFactory)
});
}
}

7.2 钩子

  • beforeResolve: 当遇到新的依赖项请求时调用。可以通过返回 false 来忽略依赖项。否则,返回 undefined 以继续。

  • factorize: 在初始化解析之前调用。它应该返回 undefined 以继续。

  • resolve: 在请求被解析之前调用。可以通过返回 false 来忽略依赖项。返回一个模块实例将结束进程。否则,返回 undefined 以继续。

  • resolveForScheme: 在解析符合统一资源标志符方案(URI)的请求之前调用。

  • afterResolve: 在请求解析后调用。

  • createModule: 在创建 NormalModule 实例之前调用。

  • module: 在创建 NormalModule 实例后调用。

  • createParser: 在 Parser 实例创建之前调用。parserOptionsmodule.parser 中对应标识符或空对象的选项。

  • parser: 在创建 Parser 实例后触发。

  • createGenerator: 在 Generator 实例创建之前调用。generatorOptionsmodule.parser 中对应标识符或空对象的选项。

  • generator: 在 Generator 实例创建之后调用。

八、compiler.hooks.contextModuleFactory


Compiler 使用 ContextModuleFactory 模块从 webpack 独特的 require.context API 生成依赖关系。它会解析请求的目录,为每个文件生成请求,并依据传递来的 regExp 进行过滤。最后匹配成功的依赖关系将被传入 NormalModuleFactory

8.1 语法

class ContextModuleFactoryPlugin{
apply(compiler){
compiler.hooks.contextModuleFactory.tap('Plugin ContextModuleFactory', (contextModuleFactory)=>{
console.log("contextModuleFactory",contextModuleFactory)
});
}
}

8.2 钩子

  • beforeResolve: 在解析请求的目录之前调用。请求可以通过返回 false 来忽略。

  • afterResolve: 在请求的目录解析后调用。

  • contextModuleFiles: 读取目录内容后调用。在递归模式下,也会读取每个子目录。回调参数是一个包含每个目录中所有文件和文件夹名称的数组。

  • alternativeRequests: 在创建请求之后但依据 regExp 进行过滤之前,为每个文件调用。

九、compiler.hooks.contextModuleFactory.hooks.parse


parse 实例, 在 compiler 中被发现,是用来解析由 webpack 处理过的每个模块。parser 也是扩展自 tapablewebpack 类 并且提供多种 tapable 钩子。

9.1 语法

class NormalModuleFactoryPlugin{
apply(compiler){
compiler.hooks.normalModuleFactory.tap('Plugin NormalModuleFactory', (normalModuleFactory)=>{

const hook = normalModuleFactory.hooks.parser.for('javascript/auto');

hook.tap('MyPlugin', (parser,options)=>{
parser.hooks.someHooks.tap();
});
});
}
}

9.2 钩子

  • evaluateTypeof:

  • evaluate:

  • evaluateIdentifier:

  • evaluateDefinedIdentifier

  • evaluateCallExpressionMember

  • statement

  • statementIf

  • label

  • import

  • importSpecifier

  • export

  • exportImport

9.3 编写

1. DonePlugin 会将模块中所有的 statementIf 节点的判断表达式修改称为 false

const t = require('@babel/types');
const g = require('@babel/generator').default;
const ConstDependency = require('webpack/lib/dependencies/ConstDependency');

class DonePlugin {
apply(compiler) {
// 解析模块时进入
compiler.hooks.normalModuleFactory.tap('pluginA', (factory) => {
// 当使用javascript/auto处理模块时会调用该hook
const hook = factory.hooks.parser.for('javascript/auto');

// 注册
hook.tap('pluginA', (parser) => {
parser.hooks.statementIf.tap('pluginA', (statementNode) => {
const { code } = g(t.booleanLiteral(false));
const dep = new ConstDependency(code, statementNode.test.range);
dep.loc = statementNode.loc;
parser.state.current.addDependency(dep);
return statementNode;
});
});
});
}
}

module.exports = DonePlugin;

十、compiler.hooks.done


compiler.hooks.done 在编译完成后触发。参数为 stats 对象,包含编译过程中的各类统计信息。