webpack 源码分析(四)——complier模块

上一篇我们看到,webpack-cli 通过 yargs 对命令行传入的参数和配置文件里的配置项做了转换包装,然后传递给 webpack compiler` 模块去编译。

这一篇我们来看看 compiler 做了些什么事。

入口

首先,我们来看看 webpack 是在什么地方引入 Compiler 这个模块的,在 webpack/lib/webpack.js 中我们发现,这个文件第一行就引入了它,并在下面的主要逻辑中使用了它:

const Compiler = require("./Compiler");……/*
* options:这就是由 webpack-cli 转换包装后的配置项
* callback:第二篇分析时,发现 `webpack-cli` 只是传入了 options对象来获取 `compiler` 对象,并没有传入回调
*/const webpack = (options, callback) => {// 验证配置项的格式// webpackOptionsSchema 是一个json格式的描述文件,它描述了webpack可接受的所有配置项及其格式// options 是用户定义的webpack.config.*.js中导出的所有配置项// validateSchema 使用 ajv 包,根据 webpackOptionsSchema 中定义的数据类型和描述来校验 options 中的各项配置项,最后返回一个错误对象,其中包含所有错误的配置项及说明const webpackOptionsValidationErrors = validateSchema(webpackOptionsSchema,options);// 如果存在配置项错误,则抛出所有错误if (webpackOptionsValidationErrors.length) {throw new WebpackOptionsValidationError(webpackOptionsValidationErrors);}//判断配置项是否数组,如果是数组则使用MultiCompiler进行编译,否则使用Compiler模块进行编译,一般情况下,我们的 options 都是对象不是数组let compiler;if (Array.isArray(options)) {compiler = new MultiCompiler(options.map(options => webpack(options)));} else if (typeof options === "object") {//使用默认配置项处理输入配置项// WebpackOptionsDefaulter 继承自 OptionsDefaulter 类// 在这个类里有两个对象://    * this.defaults : 用来存放配置项的值//  * this.config : 用来存放配置项的值的类型// process 方法的处理逻辑://     * 如果defaults对象中存在,但在config对象中不存在,并且在options中也不存在,就从 defaults对象中复制一份到options中//  * 如果在config对象中存在并且值为 “call”,则说明它的值是一个方法调用,就直接调用,并将options作为参数传入//  * 如果在config对象中存在且值为 “make”,并且在 options中没有,则说明它是一个方法,就直接调用它,并将options作为参数传入,拿到返回值,赋值给options中对应的项//  * 如果在config对象中存在切值为 “append”,则取出options中对应的值,如果它不是数组,就把它重置为数组,并且把defaults对象中的值复制到数组中,最后将这个数组作为值赋值给options相应的项options = new WebpackOptionsDefaulter().process(options);// new 一个compiler实例,参数为当前执行node命令的目录路径// Compiler类继承自我们上一篇讲过的Tapable类,在构造函数中,初始化了各种类型的钩子实例// compiler 类的内部逻辑,后面详解++++++++++++++++++++++compiler = new Compiler(options.context);// WebpackOptionsDefaulter类处理后返回的options 复制给 compilercompiler.options = options;// 使用NodeEnviromentPlugin 类给compiler添加文件输入输出的能力// NodeEnviromentPlugin的内部逻辑,后面详解++++++++++++++++++++++++++new NodeEnvironmentPlugin().apply(compiler);// 如果配置项中有插件配置并且插件配置为数组// 遍历插件数组,如果插件是一个函数,则使用complier来调用它,,并且将compier作为参数出入// 否则,使用 WebpackPluginInstance 的 apply 方法来返回一个 void 值(相当于undefined)if (options.plugins && Array.isArray(options.plugins)) {for (const plugin of options.plugins) {if (typeof plugin === "function") {plugin.call(compiler, compiler);} else {plugin.apply(compiler);}}}// 触发 environment 同步钩子,这里的 call() 是我们讲过的Tapable钩子事件的触发方法// environment 准备好之后,执行插件compiler.hooks.environment.call();// 触发 afterEnvironment 同步钩子// environment 安装完成之后,执行插件compiler.hooks.afterEnvironment.call();// 使用 WebpackOptionsApply 类处理选项,返回处理过的选项对象// WebpackOptionsApply 的处理逻辑,后面详解++++++++++++++compiler.options = new WebpackOptionsApply().process(options, compiler);} else {//如果配置既不是数组类型也不是对象类型,抛出错误throw new Error("Invalid argument: options");}// 如果传入了回到函数if (callback) {// 如果回调不是函数,抛出参数类型错误if (typeof callback !== "function") {throw new Error("Invalid argument: callback");}// 如果是监听模式,或者(配置是数组且数组中的项中有监听选项为true),则初始化监听配置,最后返回compiler实例的监听方法// 实例的监听方法会返回一个 Watching类的实例,它本质上是一个观察者if (options.watch === true ||(Array.isArray(options) && options.some(o => o.watch))) {const watchOptions = Array.isArray(options)? options.map(o => o.watchOptions || {}): options.watchOptions || {};return compiler.watch(watchOptions, callback);}// 使用compiler的run方法运行回调compiler.run(callback);}//返回compiler实例return compiler;
};

通过以上源码分析,我们得知,在 webpack() 中,实际上总共做了三件事:

  1. 对参数进行校验和规范化处理;
  2. new 一个编译器实例并且初始化各种tapable钩子,并且在环境准备好和安装完成后执行响应的钩子
  3. 初始化监听。

在上面的分析中,我们看到最核心的其实就是compiler实例,接下来我们就看下它的类的内部逻辑。

compiler 分析

主体结构

首先,我们来看主要结构:

/*…… *// 各种引入// Compiler 类继承自Tapable
class Compiler extends Tapable {constructor(context) {……}//构造函数执行各种初始化操作watch(watchOptions, handler) {……}//监听初始化run(callback) {……}// 运行编译runAsChild(callback) {...} // 作为子编译进程运行purgeInputFileSystem() {...} // 净化输入emitAssets(compilation, callback) {...} // 发布资源emitRecords(callback) {...} // 发布记录readRecords(callback) {...} // 读取记录createChildCompiler(compilation,compilerName,compilerIndex,outputOptions,plugins) {...} // 创建子编译器isChild() {return !!this.parentCompilation;} // 是否子汇编createCompilation() {return new Compilation(this);} // 创建汇编实例newCompilation(params) {return compilation;} // 根据参数创建新的汇编实例createNormalModuleFactory() {return normalModuleFactory;} // 创建普通模块的工厂createContextModuleFactory() {return contextModuleFactory;} // 创建上下文模块的工厂newCompilationParams() {return params;} // 获取一个新的汇编参数对象compile(callback) {} // 编译
}module.exports = Compiler;class SizeOnlySource extends Source {} // 定义了一个类,作用是+++++++++++

下一讲,我们将逐个攻破

webpack 源码分析(四)——complier模块相关推荐

  1. webpack 源码分析系列 ——loader

    想要更好的格式阅读体验,请查看原文:webpack 源码分析系列 --loader 为什么需要 loader webpack是一个用于现代 JavaScript 应用程序的静态模块打包工具.内部通过构 ...

  2. elasticsearch源码分析之search模块(server端)

    elasticsearch源码分析之search模块(server端) 继续接着上一篇的来说啊,当client端将search的请求发送到某一个node之后,剩下的事情就是server端来处理了,具体 ...

  3. Scrapy 源码分析之 RetryMiddleware 模块

    这是「进击的Coder」的第 689 篇技术分享 作者:TheWeiJun 来源:逆向与爬虫的故事 " 阅读本文大概需要 13 分钟. " 时隔一个多月,scrapy 章节又迎来了 ...

  4. FreeCAD源码分析:FreeCADGui模块

    FreeCAD源码分析:FreeCADGui模块 济南友泉软件有限公司 FreeCADGui项目实现了界面操作.模型显示与交互等相关功能,项目构建生成FreeCAD(_d).dll动态链接库. Fre ...

  5. Spring 源码分析(四) ——MVC(二)概述

    随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) from:Spring 源码分析(四) --MVC(二)概述 - 水门-kay的个人页面 - OSCHINA ...

  6. elasticsearch源码分析之search模块(client端)

    elasticsearch源码分析之search模块(client端) 注意,我这里所说的都是通过rest api来做的搜索,所以对于接收到请求的节点,我姑且将之称之为client端,其主要的功能我们 ...

  7. ABP源码分析四十七:ABP中的异常处理

    ABP源码分析四十七:ABP中的异常处理 参考文章: (1)ABP源码分析四十七:ABP中的异常处理 (2)https://www.cnblogs.com/1zhk/p/5538983.html (3 ...

  8. 【投屏】Scrcpy源码分析四(最终章 - Server篇)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

  9. gSOAP 源码分析(四)

    gSOAP 源码分析(四) 2012-6-2 邵盛松 前言 本文主要说明gSOAP中对Client的认证分析 gSOAP中包含了HTTP基本认证,NTLM认证等,还可以自定义SOAP Heard实现认 ...

最新文章

  1. 重磅直播|计算深度分割技术的实现与全局效应下的结构光三维重建
  2. 第10章 图与网络优化
  3. 疫情当前,宅家学习不无聊,AI视频课程资源盘点
  4. 【maven】修改编译得到的文件名
  5. “美登杯”上海市高校大学生程序设计邀请赛 **D. 小花梨的取石子游戏**
  6. matlab五角星的二维作图,MATLAB二维画图综合实例
  7. mini web框架-2-显示页面
  8. python set 和 ^ 的妙用
  9. 多数iPhone应用程序的不足之处
  10. 【软件设计师考试】《软件设计师教程》(一)
  11. matlab中进行多行注释
  12. 快速添加百度网盘文件到Aria2 猴油脚本
  13. 金蝶K3销售订单批量库存查询功能开发
  14. Java动态代理机制原理详解(JDK 和CGLIB,Javassist,ASM)
  15. 传统蓝牙HCI Command(蓝牙HCI命令)详细介绍
  16. html如何快速转pDF,如何快速实现pdf转html网页文件 高能方法帮你解决80%职场难题...
  17. PhotoShop一键修改4的倍数图片工具
  18. 挂耳式蓝牙耳机哪家的好用,推荐几款实用的挂耳式耳机
  19. Android手机电池耐用吗,八款超长待机的智能手机 大容量电池十分耐用
  20. 【考研英语——刘晓燕语法、简单句和长难句笔记】

热门文章

  1. Unity SF上的2017全球游戏果酱
  2. 继母无情,可怜的小孩!
  3. 如何在本地服务器部署网站
  4. [有点意思]通天塔导游:各种编程语言的优缺点
  5. 虚拟主机和独立服务器的区别116.211.147.x
  6. 【宅客锋评】居e安和WeCool安防摄像头对比测评:都能用,但都够不友好
  7. 设计了一个支撑 数亿 用户的系统
  8. socket: too many open files
  9. sdut_2139 数据结构实验之图论五:从起始点到目标点的最短步数(BFS)
  10. C++设计一个Bank类,实现银行的创建账户存钱取钱查询交易明细