关于javascript:webpack-流程解析3-创建compilation对象

前言

webpack初始化实现之后,则会通过传入的options.watch来判断是否要开启watch,如果开启watch则会执行watch的流程,如果是run,则会执行run的流程,本系列只关注主线,所以咱们间接从run开始,watch感兴趣的同学能够自行钻研钻研

compiler.run()

间接看外围代码

const run = () => {
    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);
            });
        });
    });
};

简略说就是做了这么几件事。

  • 触发beforeRun的回调
  • 触发run的回调
  • 而后调this.readRecords
  • readRecords的回调里调用this.compile(onCompiled)开启编译

咱们一步一步看
beforeRun会触发之前在NodeEnvironmentPlugin中注册的 beforeRun 钩子,这个plugin会判断inputFileSystem是否被配置,如果没有配置则执行purge清理办法。

readRecords会读取一些统计信息,因为没有配置recordsInputPath,这里会把this.records初始为{}

创立compilation实例

接下来就执行到compiler.compiler()办法。
compiler.compiler办法贯通了整个编译过程。首先compiler实例化了一个compilation

compile(callback) {
    const params = this.newCompilationParams();
    this.hooks.beforeCompile.callAsync(params, err => {
        if (err) return callback(err);
        this.hooks.compile.call(params);
        const compilation = this.newCompilation(params);
        // do something
    }
}

获取参数

newCompilationParams() {
    const params = {
        normalModuleFactory: this.createNormalModuleFactory(),
        contextModuleFactory: this.createContextModuleFactory()
    };
    return params;
}

参数有两个,一个是NormalModuleFactory的实例,一个是ContextModuleFactory的实例。ContextModuleFactory这个参数我在compilation外面没搜到,暂且略过,后续打个断点看会不会走进来。这里次要看下NormalModuleFactory

NormalModuleFactory

先看下实例化NormalModuleFactory的参数

const normalModuleFactory = new NormalModuleFactory({
    context: this.options.context,
    fs: this.inputFileSystem,
    resolverFactory: this.resolverFactory,
    options: this.options.module,
    associatedObjectForCache: this.root,
    layers: this.options.experiments.layers
});

留神这里的resolverFactory, 当前会用到。
接下来看下new NormalModuleFactory的时候产生了啥

constructor({
    context,
    fs,
    resolverFactory,
    options,
    associatedObjectForCache,
    layers = false
}) {
    super();
    this.hooks = 定义了很多hooks
    this.resolverFactory = resolverFactory;
    this.ruleSet = ruleSetCompiler.compile([
            {
                rules: options.defaultRules
            },
            {
                rules: options.rules
            }
    ]);
    this.context = context || "";
    this.fs = fs;
    this._globalParserOptions = options.parser;
    this._globalGeneratorOptions = options.generator;
    /** @type {Map<string, WeakMap<Object, TODO>>} */
    this.parserCache = new Map();
    /** @type {Map<string, WeakMap<Object, Generator>>} */
    this.generatorCache = new Map();
    /** @type {Set<Module>} */
    this._restoredUnsafeCacheEntries = new Set();

    const cacheParseResource = parseResource.bindCache(
        associatedObjectForCache
    );
    this.hooks.factorize.tapAsync(
        // do something
    );
    this.hooks.resolve.tapAsync(
        // dosomething
    );
}

可能感觉太长了不看,我间接给大家翻译一下干了啥

  • 定义了很多外部的hook,比方说最初注册的两个 reslover,factorize
  • 定义了很多构建module须要的变量,这里先不细说。
  • 同时注册了两个NormalModuleFactory的外部 hook。会在适合的机会在被compilation对象调用

new NormalModuleFactory()之后,触发了compiler上的normalModuleFactory钩子

this.hooks.normalModuleFactory.call(normalModuleFactory);

持续触发钩子回调

而后触发beforeCompilecompile钩子。

开始实例化

newCompilation(params) {
    const compilation = this.createCompilation(params); //这里简略了解为new 了一下,
    compilation.name = this.name;
    compilation.records = this.records;
    this.hooks.thisCompilation.call(compilation, params);
    this.hooks.compilation.call(compilation, params);
    return compilation;
}

分类一下,这个函数做了两件事。

  • new Compilation,再赋一点值
  • 注册两个钩子

    new Compilation 外部细节

    Compilation对象示意了以后模块资源、编译生成资源、变动的文件、以及被跟踪依赖的状态信息,代表了一次资源的构建。constructor代码太多就不贴在这里了,大家能够自行去看看。

简略总结一下,就是

  • 在compilation外部注册了很多外部的钩子。
  • 初始化了一些本身属性
  • 实例化MainTemplate,ChunkTemplate,HotUpdateChunkTemplate, RuntimeTemplate, ModuleTemplate。用于提供编译模板

实例化后的钩子调用

this.hooks.thisCompilation.call(compilation, params);
this.hooks.compilation.call(compilation, params);

compilation钩子的调用,会调用到之前在entryplugin注册的办法。
会往dependencyFactories里减少依赖模块。

compilation.dependencyFactories.set(
        EntryDependency,
        normalModuleFactory
);

兴许你好奇,这里为什么有两个钩子?起因是跟子编译器无关。在 Compiler 的 createChildCompiler 办法里创立子编译器,其中 thisCompilation 钩子不会被复制,而 compilation 会被复制。
子编译器领有残缺的 module 和 chunk 生成,通过子编译器能够独立于父编译器执行一个外围构建流程,额定生成一些须要的 module 和 chunk。

完结

到目前为止,形容构建流程的对象compiler和形容编译过程的对象compilation 对象曾经创立实现。下一篇文章咱们进入构建流程。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理