webpack源码探索(三)

424 阅读2分钟

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战

前言

我们在上一文中了解到webpack.js中的createCompiler方法,在这个方法中,调用了Compiler类进行实例化,今天我们就针对Compiler进行详细的研究。

源码研究

1. constructor方法

使用了核心库tapable进行初始化hook,其实就是相当于浏览器环境注册了大量的addEventListener,然后后续webpack打包构建的不同的流程触发对应的hook。

    this.hooks = Object.freeze({
        initialize: new SyncHook([]),
        ...
        beforeRun: new AsyncSeriesHook(["compiler"]),
        run: new AsyncSeriesHook(["compiler"]),
        ...
        beforeCompile: new AsyncSeriesHook(["params"]),
        compile: new SyncHook(["params"]),
        ...
    });

通过这里我们其实就可以get到一个技能,通过webpack创建compiler实例之后,然后通过compiler暴露出的不同钩子监听不同的阶段,然后做出对应的操作。当然,不同的钩子访问方式略微有些区别,取决于钩子的注册方式。

    compiler.hooks.someHook.tap(...)

在constructor方法中进行了大量的属性初始化。

run 方法

为了防止重复构建,通过running属性来标记构建中状态,如果调用run方法的时候正在进行构建 ,那么就抛出错误:You ran Webpack twice. Each instance only supports a single concurrent compilation at a time.

    if (this.running) {
        return callback(new ConcurrentCompilationError());
    }

在这个方法内部中,重新定义了一个run的方法。在这个方法中触发了beforeRun和run两个钩子,同时在run钩子的回调中触发了compiler方法。在run方法中定义了一个成功的回调onCompiled和一个失败的回调finalCallback,在这两个回调中触发了对应的构建节点的钩子。

    this.hooks.beforeRun.callAsync(this, err => {
        if (err) return finalCallback(err);

        this.hooks.run.callAsync(this, err => {
            if (err) return finalCallback(err);

            this.readRecords(err => {
                if (err) return finalCallback(err);

                this.compile(onCompiled);
            });
        });
    });

compiler 方法

在compiler方法中触发了beforeCompile、compile、make、finishMake和afterCompile钩子。通过这个几个钩子可以看出来,这个方法核心就是进行compiler。在beforeCompile钩子的回调中,通过实例化Compilation来创建了新的编译器compilation。当然了,这里其实有个小细节,在createCompilation的时候,先清除了旧的compilation。最后通过compilation.finish定义了回调方法来接收构建结果。

在整个方法中通过callback方法将构建结果通知给webpack,也就是前面提到的onCompiled方法。

    this.hooks.beforeCompile.callAsync(params, err => {
        this.hooks.compile.call(params);
            const compilation = this.newCompilation(params);
                this.hooks.make.callAsync(compilation, err => {
                    if (err) return callback(err);
                    this.hooks.finishMake.callAsync(compilation, err => {
                        if (err) return callback(err);
                        compilation.finish(err => {callback(null, compilation)});
                    });
                });
            });
        });
    });

结尾

好的,到这里Compiler的核心逻辑就基本看完了,接下来我们就会继续研究核心类Compilation。欢迎小伙伴们在下方留言交流。