webpack|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>} */ this.parserCache = new Map(); /** @type {Map>} */ this.generatorCache = new Map(); /** @type {Set} */ 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。
结束 【webpack|webpack 流程解析(3) 创建compilation对象】到目前为止,描述构建流程的对象compiler和描述编译过程的对象compilation 对象已经创建完成。下一篇文章我们进入构建流程。

    推荐阅读