53 webpack原理-Compiler文件分析

Compiler文件分析

之前说到Webpack把构建任务交给了Compiler进行处理,选取Compiler源码进行分析,看看具体做了一些什么,精简过后的源码如下:

"use strict";
// 省略其他引入
const {
    Tapable,
    SyncHook,
    SyncBailHook,
    AsyncParallelHook,
    AsyncSeriesHook
} = require("tapable");

const Compilation = require("./Compilation");
const Stats = require("./Stats");
const Watching = require("./Watching");
// 省略其他引入
class Compiler extends Tapable {
    constructor(context) {
        super();
        // 定义生命周期钩子函数
        this.hooks = {
            shouldEmit: new SyncBailHook(["compilation"]),
            done: new AsyncSeriesHook(["stats"]),
            additionalPass: new AsyncSeriesHook([]),
            beforeRun: new AsyncSeriesHook(["compiler"]),
            run: new AsyncSeriesHook(["compiler"]),
            emit: new AsyncSeriesHook(["compilation"]),
            assetEmitted: new AsyncSeriesHook(["file", "content"]),
            afterEmit: new AsyncSeriesHook(["compilation"]),
            thisCompilation: new SyncHook(["compilation", "params"]),
            compilation: new SyncHook(["compilation", "params"]),
            normalModuleFactory: new SyncHook(["normalModuleFactory"]),
            contextModuleFactory: new SyncHook(["contextModulefactory"]),
            beforeCompile: new AsyncSeriesHook(["params"]),
            compile: new SyncHook(["params"]),
            make: new AsyncParallelHook(["compilation"]),
            afterCompile: new AsyncSeriesHook(["compilation"]),
            watchRun: new AsyncSeriesHook(["compiler"]),
            failed: new SyncHook(["error"]),
            invalid: new SyncHook(["filename", "changeTime"]),
            watchClose: new SyncHook([]),
            infrastructureLog: new SyncBailHook(["origin", "type", "args"]),
            environment: new SyncHook([]),
            afterEnvironment: new SyncHook([]),
            afterPlugins: new SyncHook(["compiler"]),
            afterResolvers: new SyncHook(["compiler"]),
            entryOption: new SyncBailHook(["context", "entry"])
        };
        this.name = undefined;
        this.parentCompilation = undefined;
        this.outputPath = "";
        this.outputFileSystem = null;
        this.inputFileSystem = null;
        this.recordsInputPath = null;
        this.recordsOutputPath = null;
        this.records = {};
        this.removedFiles = new Set();
        this.fileTimestamps = new Map();
        this.contextTimestamps = new Map();
        this.resolverFactory = new ResolverFactory();
        this.infrastructureLogger = undefined;
        this.options = /** @type {webpackOptions} */ ({});
        this.context = context;
        this.requestShortener = new RequestShortener(context);
        this.running = false;
        this.watchMode = false;
        this._assetEmittingSourceCache = new WeakMap();
        this._assetEmittingWrittenFiles = new Map();
    }
    // 文件监听
    watch(watchOptions, handler) { }
    // 开始执行一次新的编译
    run(callback) {
        // 此次编译完成
        const onCompiled = (err, compilation) => {
            // 输出资源
            this.emitAssets(compilation, err => {
                // 输出编译记录
                this.emitRecords(err => { });
            });
        };
        // 发布开始编译之前的信息,订阅该钩子函数的此时可以做一些事情,比如loader,plugin......
        this.hooks.beforeRun.callAsync(this, err => { });
    }
    // 输出资源
    emitAssets(compilation, callback) {
        // 发布输出资源的信息,订阅该钩子函数的此时可以做一些事情,比如loader,plugin......
        this.hooks.emit.callAsync(compilation, err => { });
    }
    // 输出编译记录
    emitRecords(callback) { }
    // 读取记录
    readRecords(callback) { }
    createCompilation() {
        return new Compilation(this);
    }
    // 获取compilation实例
    newCompilation(params) { }
    // 编译
    compile(callback) {
        // 发布开始编译之前的信息,订阅该钩子函数的此时可以做一些事情
        this.hooks.beforeCompile.callAsync(params, err => { });
    }
}
module.exports = Compiler;

可以看出Compiler类的工作流程总的可以概括为以下三个阶段:

  • 初始化
  • 编译
  • 输出

这三个阶段是一次执行所要依次经历的阶段,如果开启了监听模式,首先还是会执行一遍上面三个阶段,监听到文件发生变化以后,会再次依次执行编译阶段和输出阶段。

因为Compiler集成自Tapable类,所以在不同阶段触发不同的生命周期钩子函数,也就是发生事件,下面详细介绍一下各个阶段发生的事件。

初始化阶段

在这里插入图片描述

编译阶段

在这里插入图片描述
在这里插入图片描述
Compilation是编译阶段中最重要的事件,因为Compilation阶段调用了Loader去转换模块,Compilation阶段也会发生很多事件。

输出阶段

在这里插入图片描述
输出阶段得到了各个模块经过转换后的结果和其依赖关系,并且将相关模块组合成一个个Chunk,然后根据Chunk的类型,使用对应的模版生成最终要输出的文件内容。

通过上面三个阶段的分析,可以总结出Webpack从启动到结束会经历以下流程:

  • 初始化参数:根据配置文件和Shell语句中从读取合并参数,得到最终的参数。
  • 开始编译:通过参数初始化Compiler对象,加载所有配置的插件,调用compiler的run/watch方法开始执行编译。
  • 确定入口:根据配置中的entry确定入口。
  • 编译模块:从入口文件开始,调用所配置的Loader对模块进行解析转换,再递归找出模块依赖的模块,重复执行本步骤,直到所有入口依赖的模块都得到本步骤处理。
  • 完成模块编译:根据上一步的操作,得到所有模块被转换后的内容以及它们之间的依赖关系。
  • 输出资源:根据入口和模块之间的依赖关系,组成一个个包含多个模块的Chunk,再将每个Chunk转换成一个单独的文件加入到输出列表,这是修改输出内容最后的机会。
  • 输出完成:确定好输出内容后,根据配置的输出文件名称和路径,将文件的内容写入到系统中。

了解Webpack的工作流程,是进阶Wbepack必经的步骤,在遇到构建问题和想要配置功能更加丰富的时候,才能从根本上去解决问题和输出工作方案。

本章节着重讲了Compiler和Comilation,是因为它们负责Webpack的主要功能,除了上面介绍的以后,还有很多对象在Webpack中扮演者重要的角色,比如负责模块路径处理的Resolver和解析模块的Parser(参考7.4,7.5小节)。这些对象都继承自Tapable,可以发布许多事件,构成庞大的发布/订阅的系统,为插件的扩展提供可能,极大地丰富了Webpack的可能性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值