Webpack源码深入-compiler

compiler

上述中执行cli.run(arg)就是执行webpack-cli.js中的run方法,在执行的过程中,会加载webpack/lib/webpack.js中,得到this.webpack模块,然后运行this.runWebpack()方法,创建compiler对象

创建compiler

创建compiler的工作是由webpack/lib/webpack.js模块完成的。
基本代码结构为

const webpack = (options, callback) => {const create = () => {};if (callback) {// 传递 callback 的情况try {const { compiler, watch, watchOptions } = create();if (watch) {compiler.watch(watchOptions, callback);} else {compiler.run((err, stats) => {});}return compiler;} catch (err) {}} else {// 未传递 callback 的情况const { compiler, watch } = create();return compiler;}
}
  • options: 用户传入创建编译器实例的配置项
  • callback:处理webpack编译工作的回调函数
    代码流程为: 声明具体的compiler中的create方法,该方法创建了compiler逻辑,然后判断传入callback决定是否需要启动编译(compiler.run)。传入callback,调用私有的create方法创建compiler对象,接下来判断是否为watch模式,如果是则调用compiler.watch,否则调用comiler.run启动编译。如果没有传入callback,则说明不需要启动编译,在创建compiler对象后返回。

create方法

const create = () => {if (!asArray(options).every(webpackOptionsSchemaCheck)) {getValidateSchema()(webpackOptionsSchema, options);util.deprecate(() => {},"webpack bug: Pre-compiled schema reports error while real schema is happy. This has performance drawbacks.","DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID")();}/** @type {MultiCompiler|Compiler} */let compiler;/** @type {boolean | undefined} */let watch = false;/** @type {WatchOptions|WatchOptions[]} */let watchOptions;if (Array.isArray(options)) {/** @type {MultiCompiler} */compiler = createMultiCompiler(options,/** @type {MultiCompilerOptions} */ (options));watch = options.some(options => options.watch);watchOptions = options.map(options => options.watchOptions || {});} else {const webpackOptions = /** @type {WebpackOptions} */ (options);/** @type {Compiler} */compiler = createCompiler(webpackOptions);watch = webpackOptions.watch;watchOptions = webpackOptions.watchOptions || {};}return { compiler, watch, watchOptions };
};

create方法做了如下的工作。

  1. 校验用户传递的options是否符合webpack内部的约定好的schema。
  2. 判断options的类型,根据不同的类型,执行不同的操作
    1. array。创建多项目编译器。判断这些项目是否需要存在watch模式,然后获得各个配置项中的watchOption配置
    2. 非数组。调用createCompiler创建编译器的实例。然后获取watch模式以及watchOptions的配置项目。
  3. 返回compiler, watch, watchOptions

createCompiler方法

createCompiler函数创建了一个编译器对象,并根据传入的rawOptions配置对compiler对象进行初始化。

const createCompiler = (rawOptions, compilerIndex) => {const options = getNormalizedWebpackOptions(rawOptions);applyWebpackOptionsBaseDefaults(options);const compiler = new Compiler(/** @type {string} */ (options.context),options);new NodeEnvironmentPlugin({infrastructureLogging: options.infrastructureLogging}).apply(compiler);if (Array.isArray(options.plugins)) {for (const plugin of options.plugins) {if (typeof plugin === "function") {/** @type {WebpackPluginFunction} */(plugin).call(compiler, compiler);} else if (plugin) {plugin.apply(compiler);}}}
}
  1. getNormalizedWebpackOptions格式化rawOptions,经过处理webpack的标准配置对象,抹平各种语法的配置对象。
  2. 根据上文得到的配置对象,调用applyWebpackOptionsBaseDefaults进行配置。
  3. 创建compiler对象实例。传入上文中得到的配置对象。
  4. 初始化NodeEnvironmentPlugin插件,这个插件是Compiler工作zhon 一个模块,负责文件io,监听文件内容的改变。由四个基本文件系统组成。nputFileSystem、outputFileSystem 处理文件i/o,watchFileSystem 监听文件改动,intermediateFileSystem则处理所有不被看做是输入或输出文件系统操作,比如写记录,缓存或者分析输出等。
    5.初始化插件调用,webpack的插件以及自定义插件上定义的apply都是在这个时候调用的,调用这些钩子会传入compiler对象,通过这个对象可以注册全局声明周期钩子
new NodeEnvironmentPlugin({infrastructureLogging: options.infrastructureLogging
}).apply(compiler);
  1. new WebpackOptionsApply().process(options, compiler);wanchengcompiler特性的应用工作。
  2. compiler.hooks.initialize.call();触发 compiler.hooks.environment、compiler.hooks.afterEnvironment、compiler.hooks.initialize 钩子。
  3. 返回compiler对象

WebpackOptionsApply

位置: webpack/lib/webpackOptionsApply.js

JavascriptModulesPlugin

javascriptMoudlesPlugin的作用是注册各种模块的JavaScript的解析器以及JavaScript产物生成器。分别注册在normalModuleFactory.hooks.createParser和normalModuleFactory.hooks.createGenertor钩子上。

JsonModulesPlugin

JsonModulesPlugin工作和前面的JavaScriptModulesPlugin类似,JsonModulesPlugin注册是JSON模块的解析器和内容生成器

EntryOptionPlugin

EntryOptionPlugin是处理的entry注册的插件,这个是会在webpack.config.js中作为入口,根据不同类型的入口声明选择DynamicEntryPlugin或者Entryplugin进行入口的注册,入口也是一个编译的起始模块,webpack会从指定的入口开始构建模块并产出依赖图谱。最终生成bundle

WebAssemblyModulesPlugin

WebAssswmblyModulesPlugin是注册webAsswmbly模块的解析器和内容生成器。

ModuleConcateNationPlugin

通过这个插件,将在打包的时候将所有的模块的预编译到一个闭包中,以提升代码在浏览器中的执行速度。=> “作用域提升”

SplitChunkPlugin

拆分chunk的时候,按照一定的规则将一个较大的chunk拆分称为多个较小的chunk。

if(options.cache&&typeof options.cache === “object”)

options.cache: 设置该选项,webpack构建将会在初次构建的时候写入缓存并在之后构建中优先使用缓存中的内容,目的是webpack答复提升构建速度,缓存中包括模块,request解析结构,loader等构建缓存等多项中间产物的缓存。

AddBuildDependenciesPlugin

这个插件是将options.cache.buildDependencies主动声明的构建依赖项目假如到webpack收集的构建依赖范围中。默认收集loader等node_modules下的内容作为构建依赖。

IdleFileCachePlugin

处理webpack缓存的写入和恢复的插件,只负责调度。

PackFileCacheStrategy

配合上面的IdleFileCachePlugin插件使用的,这里面定义缓存的具体读写校验逻辑。

ResolverCachePlugin

webpack的构建的耗时很多一部分都花费在了resolve中,这是webpack优化编译的时候的手段。

  1. 统计缓存利用率
  2. 拦截resolve过程,优先使用缓存,如果缓存有效则直接返回缓存结果,否则进行常规的resolve过程。
compiler.resolverFactory.hooks.resolveOptions.for()

为webpack内建的三种模块注册路径解析器的配置项

  1. normal: NormalModule的resolver创建的是所需的配置项
  2. loader: loader模块resolver创建的时候需要的配置项
  3. context: ContextModule模块的resolver创建时所需要的配置项

compiler对象

位置: compiler

核心参数:

  1. context: 构建的上下文目录,即运行构建命令的工作目录
  2. options: options选项对象

方法

  1. run: 常规启动webpack编译,一次编译,输出bundle后即退出进程
  2. watch: watch模式启动编译,一次编译后不退出进程,见识到文件变化后重启webpack编程流程
  3. getCache: 该方法是创建持久化的缓存门面对象的方法
class Compiler {constructor(context, opotions = {}) {}getCache () {}
}

构造参数

	constructor(context, options =  ({})) {this.hooks = Object.freeze({initialize: new SyncHook([]),shouldEmit: new SyncBailHok(["compilation"]),done: new AsyncSeriesHook(["stats"]),afterDone: new SyncHook(["stats"]),additionalPass: new AsyncSeriesHook([]),beforeRun: new AsyncSeriesHook(["compiler"]),run: new AsyncSeriesHook(["compiler"]),emit: new AsyncSeriesHook(["compilation"]),assetEmitted: new AsyncSeriesHook(["file", "info"]),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"]),finishMake: new AsyncSeriesHook(["compilation"]),afterCompile: new AsyncSeriesHook(["compilation"]),readRecords: new AsyncSeriesHook([]),emitRecords: new AsyncSeriesHook([]),watchRun: new AsyncSeriesHook(["compiler"]),failed: new SyncHook(["error"]),invalid: new SyncHook(["filename", "changeTime"]),watchClose: new SyncHook([]),shutdown: new AsyncSeriesHook([]),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.webpack = webpack;this.name = undefined;this.parentCompilation = undefined;this.root = this;this.outputPath = "";this.watching = undefined;this.outputFileSystem = null;this.intermediateFileSystem = null;this.inputFileSystem = null;this.watchFileSystem = null;this.recordsInputPath = null;this.recordsOutputPath = null;this.records = {};this.managedPaths = new Set();this.unmanagedPaths = new Set();this.immutablePaths = new Set();this.modifiedFiles = undefined;this.removedFiles = undefined;this.fileTimestamps = undefined;this.contextTimestamps = undefined;this.fsStartTime = undefined;this.resolverFactory = new ResolverFactory();this.infrastructureLogger = undefined;this.platform = {web: null,browser: null,webworker: null,node: null,nwjs: null,electron: null};this.options = options;this.context = context;this.requestShortener = new RequestShortener(context, this.root);this.cache = new Cache();this.moduleMemCaches = undefined;this.compilerPath = "";this.running = false;this.idle = false;this.watchMode = false;this._backCompat = this.options.experiments.backCompat !== false;this._lastCompilation = undefined;this._lastNormalModuleFactory = undefined;this._assetEmittingSourceCache = new WeakMap();this._assetEmittingPreviousFiles = new Set();}
compiler.hooks
  1. initialize: SyncHook,同步钩子,触发的时候表示webpack的初始化已经完成
  2. done: AsyncSeriesHook,异步串行钩子,参数stats对象,该函数触发的时候表示webpack构建完成
  3. run: AsyncSeriesHook,异步串行钩子,参数compiler实例对象,该狗仔在run方法被调用的启动编译的时候触发
  4. emit:AsyncSeriesHook,异步串行钩子,参数compilation对象,表示在编译后期发射编译产物文件前触发。
  5. afterEmit: AsyncSeriesHook,异步串行钩子,参数compilation对象,表示该钩子在完成文件发射的时候触发。
  6. thisCompilation: SyncHook,同步钩子,参数 compilation, params,该钩子隶属于本次编译,在 compilation 钩子创建前触发,该钩子注册的插件不会被复制到子编译器;
  7. compilation: SyncHook 同步钩子,参数 compilation, params 对象,compilation 钩子在 compilation 对象被创建后触发,compilation 在整个编译声明周期内创建一次,有别于 thisComilation;
  8. normalModuleFactory: SyncHook,同步钩子,参数 normalModuleFactory 对象,实例化 NormalModuleFactory 后触发,normalModuleFactory 用创建常规模块;
  9. compile: SyncHook,同步钩子,params 用于创建 compilation 的 params,于编译启动时,创建 compilation 对象前触发;
  10. AsyncParallelHook,异步并行钩子,参数 compilation 对象,于创建 compilation 对象后触发,开启 entry 收集。EntryPlugin 就是定义在了 make 钩子上,模块的创建和构建过程主要集中在make阶段。
  11. finishMake: AsyncSeriesHook,异步串行钩子,参数 compilation 对象,在 make 阶段完成后触发;
  12. readRecords: AsyncSeriesHook,异步串行钩子,无参数,读取 records 文件后触发;
  13. emitRecords: AsyncSeriesHook,异步串行钩子,无参数,向文件系统写入 records 文件后触发;
  14. watchRun: AsyncSeriesHook,异步串行钩子,参数 compiler 对象,watch 模式下启动编译后触发;
  15. shutdown: AsyncSeriesHook,异步串行钩子,参数无,编译停止后触发,这个钩子主要是持久化缓存写入工作的信号;
  16. environment: SyncHook,同步钩子,参数无,初始化用户插件并完成 webpack 内部默认配置初始化完成后,应用 webpack 默认特性插件(new WebpackOptionsApply().process)之前触发;
文件系统声明

webpack构建过程中到依赖文件系统,没有直接引用fs模块,而是针对不同的工程设计了不同的文件系统。这些文件系统底层是依赖于fs模块的。
启动dev-server会默认将文件写好,相当于是接管了原有的文件系统写入
主要有四个文件系统

  1. outputFileSystem: 用于向文件系统中写入的场景
  2. intermediateFileSystem:这个属于webpack特有的文件系统,负责webpack构建过程中的中间产物。
  3. inputFileSystem:用于webpack文件的读取场景
  4. watchFileSystem:该文件系统封装了监听文件变化的能力,用于watch模式下的监听文件变化的文件系统。
records对象声明

records对象是webpack提供的一种用于记录编译过程信息的;对象,可以记录编译过程中的各种信息,比如模块的依赖关系,错误信息等,通过records对象,开发人员可以方便的分析和调试编译过程的问题,从而提高开发效率。

this.recordsInputPath = null;
/** @type {string|null} */
this.recordsOutputPath = null;
/** @type {Record<string, TODO>} */
this.records = {};
  1. recordInputPath: records的输入路径。
  2. recordOutputPath: records的输出路径
  3. records: 收集到的records内容存储。
resolverFactory实例化

resolverFactory适用于创建resolver[路径解析器]的工厂。

this.resolverFactory = new ResolverFactory() 
持久化缓存声明

webpack内部启用持久化缓存之后,整个构建流程中的缓存都由compiler.cache同一调度。

this.cache = new Cache()

后续构建过程中都会通过compiler.getCache方法获得相应的缓存对象,cache类型本身不负责缓存读写,cache主要负责调度,compiler.cache.hooks的触发,读写的部分是有strategy来负责。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/30979.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Microsoft Edge浏览器安装crx拓展插件教程

1、首先打开edge浏览器&#xff0c;点击顶部地址栏。 2、在地址栏中输入"edge://flags/#extensions-on-edge-urls"并按下回车。2、在地址栏中输入"edge://flags/#extensions-on-edge-urls"并按下回车。 3、进入后&#xff0c;将图示选项改为“已禁用”。 …

微信小程序轮播图

效果图 详情可见 微信小程序 参照&#xff1a;swiper | uni-app官网 代码&#xff1a; <!--轮播图-- > <swiper interval"2000" autoplay"true" circular"true" style"height: 300px;"><swiper-item style&qu…

ultralytics中常用的函数和类

Ultralytics是一个用于目标检测的开源框架&#xff0c;基于YOLO&#xff08;You Only Look Once&#xff09;模型。以下是Ultralytics中一些常用的函数和类&#xff0c;涵盖了数据加载、模型训练、推理等方面&#xff1a; 数据加载 1.LoadImages 作用&#xff1a;加载静态图…

华媒舍:8个让你东南亚媒体发稿事半功倍的方法

本文将为您介绍8个方法&#xff0c;可以帮助您在东南亚地区的媒体发稿过程中事半功倍。无论您是一名公关人员、市场营销专家还是普通的新闻工作者&#xff0c;这些方法都将对您极具帮助。 1. 了解目标受众 在东南亚地区发布媒体稿件时&#xff0c;首要的步骤是了解目标受众。不…

Python学习笔记15:进阶篇(四)文件的读写。

文件操作 学习编程操作中&#xff0c;我觉得文件操作是必不可少的一部分。不管是读书的时候学习的c&#xff0c;c&#xff0c;工作的前学的java&#xff0c;现在学的Python&#xff0c;没学过的php和go&#xff0c;都有文件操作的模块以及库的支持&#xff0c;重要性毫无疑问。…

备忘录模式(大话设计模式)C/C++版本

备忘录模式 C #include <iostream> #include <string> using namespace std;// Memento类&#xff0c;备忘录&#xff0c;此处为角色状态存储箱 class RoleStateMemento { private:int m_vit; // 生命力int m_atk; // 攻击力int m_def; // 防御力 public:RoleStat…

运算放大器(运放)输入偏置电流、失调电流

输入偏置电流定义 理想情况下&#xff0c;并无电流进入运算放大器的输入端。而实际操作中&#xff0c;始终存在两个输入偏置电流&#xff0c;即IB和IB-(参见图1)。 I B I_B IB​的值大小不一&#xff0c;在静电计AD549中低至60 fA(每三微秒通过一个电子)&#xff0c;而在某些高…

JavaScript 输出与弹框

1.console.log()&#xff1b;控制台输出 JS 中常用的调试语句&#xff0c;该语句会将内容输出到控制台的 Console 中。 <script type"text/javascript"> console.log("哈喽&#xff01;&#xff01;&#xff01;"); </script> 2.docment.writ…

Matlab数学建模实战应用:案例3 - 投资组合优化

目录 前言 一、问题分析 二、模型建立 三、Matlab代码实现 完整代码示例 四、模型验证 五、模型应用 实例示范&#xff1a;投资组合优化 步骤 1&#xff1a;导入数据并计算统计量 步骤 2&#xff1a;建立优化模型并求解 步骤 3&#xff1a;绘制有效前沿&#xff08;…

CVPR 2024揭幕,清华大学论文接收量霸榜,轻松碾压斯坦福、麻省理工

CVPR2024 会议之眼 快讯 会议介绍 2024 年 CVPR &#xff08;Computer Vision and Pattern Recogntion Conference) 即国际计算机视觉与模式识别会议&#xff0c;于6月17日至21日正在美国西雅图召开。CVPR是计算机视觉和模式识别领域的顶级会议之一。与ICCV和ECCV并称为计算…

LeetCode 平衡二叉搜索树

//平衡二叉搜索数&#xff0c;关键在于平衡两个字&#xff0c;左右数的高度差不超过1&#xff0c;搜索树&#xff0c;左节点小于 根节点 小于右节点 //先谈平衡&#xff0c;我们找到数组的中间节点作为数的根节点&#xff0c;这个方法被论证过是可行的&#xff0c;然后…

TikTok账号运营:静态住宅IP为什么可以防封?

静态住宅IP代理服务是一种提供稳定、静态IP地址并可隐藏用户真实IP地址的网络代理服务。此类代理服务通常使用高速光纤网络来提供稳定、高速的互联网体验。与动态IP代理相比&#xff0c;静态住宅IP代理的IP地址更稳定&#xff0c;被封的可能性更小&#xff0c;因此更受用户欢迎…

整理好了!2024年最常见 20 道设计模式面试题(七)

上一篇地址&#xff1a;整理好了&#xff01;2024年最常见 20 道设计模式面试题&#xff08;六&#xff09;-CSDN博客 十三、请解释桥接模式&#xff0c;并给出一个使用场景。 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;它将抽象部分与…

fastadmin完全自定义页面内容

fastadmin的后台管理页面全自动生成非常方便,但是有些复杂查询确不太好弄,需要完全自定义,但是遵从以下几个步骤即可轻松定制了 1.随便使用某个数据表生成页面所需内容 通过命令工具即可完成 2.自定义返回内容 在controller的index方法里面,删除掉原来返回的内容(主要是aja…

通过数字证书获取CRL吊销列表

在一些对安全性要求比较严格的企业,需要在每次使用证书时,要求对证书的有效性进行验证,如何验证可查看我的其他博客。其中验证的吊销列表(CRL)是可以从数字证书中获取吊销列表文件的发布地址的。 一、一般CRL的使用场景设计 证书颁发机构对CRL的发布也不是实时的,在证书…

计算机网络 —— 应用层(DHCP)

计算机网络 —— 应用层&#xff08;DHCP&#xff09; 什么是DHCPDHCP工作过程DHCP DISCOVERDHCP OFFERDHCP RQUESTDHCP ACK DHCP租约机制中继代理工作原理功能与优势 我们今天来计网的DHCP&#xff1a; 什么是DHCP DHCP&#xff08;Dynamic Host Configuration Protocol&…

2024年,收付通申请开通流程

大家好&#xff0c;今天咱们来聊聊关于APP场景中开通微信收付通的一些实用小窍门。在如今的移动互联网时代&#xff0c;很多商家都选择通过APP来提供服务和产品&#xff0c;因此如何在APP中顺利集成微信收付通功能&#xff0c;让用户能够轻松完成支付&#xff0c;就显得尤为重要…

分支结构相关

1.if 语句 结构&#xff1a; if 条件语句&#xff1a; 代码块 小练习&#xff1a; 使用random.randint()函数随机生成一个1~100之间的整数&#xff0c;判断是否是偶数 import random n random.randint(1,100) print(n) if n % 2 0:print(str(n) "是偶数") 2.else语…

Vue中使用ElementUI组件Form组件的校验validate

先准备一些el-form元素 这里面el-form中:model(v-bind:model)是单项绑定的&#xff0c;如果你写成了v-model""可能会出现校验没有效果的情况。 这是校验过后的结果了 现在开始使用下吧&#xff01; 1.在el-form中绑定一个ref&#xff0c;名字自拟,后续触发检验结果…

代码随想录算法训练营第四十四天|LeetCode198 打家劫舍、LeetCode213 打家劫舍Ⅱ

题1&#xff1a; 指路&#xff1a;198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 思路与代码&#xff1a; 对于这个题&#xff0c;拿房屋i举例&#xff0c;我们需要考虑的是否确定偷取这个房屋&#xff0c;如果确定偷取这个房屋&#xff0c;那么我们将得到房屋i的金…