webpack源码深入--- webpack的编译主流程

webpack5的编译主流程

根据watch选项调用compiler.watch或者是compiler.run()方法

try {const { compiler, watch, watchOptions } = create();if (watch) {compiler.watch(watchOptions, callback);} else {compiler.run((err, stats) => {compiler.close(err2 => {callback(err || err2,/** @type {options extends WebpackOptions ? Stats : MultiStats} */(stats));});});}return compiler;
} catch (err) {process.nextTick(() => callback(/** @type {Error} */ (err)));return null;
}

compiler.run方法

来自于Compiler.prototype.run方法webpack/lib/Compiler.js

// 代码精简过
class Compiler {constructor () {}run(callback) {   // 判断compiler.running状态,防止重复执行相同的编译任务,若处于执行状态,则抛出异常终止本次调用if (this.running) {return callback(new ConcurrentCompilationError());}// finalCallback是编译流程的最终回调,持久化缓存的写入信号就是在这里释放的。const finalCallback = (err, stats) => {};// 设置compiler.running方法为truethis.running = true;// 声明onCompiled内部方法,用于处理编译过程中的事件回调,根据编译的状态和钩子函数的返回值执行不同的操作const onCompiled = (err, compilation) => { };// 声明内部的run方法。const run = () => {};// 判断当前的空闲状态。该标识符在持久化缓存写入的时候为true,根据状态不同有不同的处理,true则需要等待缓存处理结束的会调里调用run方法启动编译。this.idle为false,则直接调用run方法if (this.idle) {this.cache.endIdle(err => {this.idle = false;run();});} else {run();}}
}

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);});});});
};

compiler.hooks.beforeRun.call

触发compiler.hooks.beforeRun钩子,传入compiler实例和回调。订阅该钩子的插件有

  1. NodeEnvironmentPlugin
class NodeEnvironmentPlugin {  
constructor(options) {  this.options = options;  
}  
apply(compiler) {  compiler.hooks.beforeRun.tap("NodeEnvironmentPlugin", compiler => {  if (compiler.inputFileSystem === inputFileSystem) {  compiler.fsStartTime = Date.now();  inputFileSystem.purge();   // 出清文件系统}  });  }  
}

如果当前的文件系统是给定的inputFileSystem,则记录当前的时间,重新编译,此前的文件系统中的内容没用了
2. ProgressPlugin

class ProgressPlugin {
// 简化后的constructor () {apply(compiler) {interceptHook(compiler.hooks.beforeRun, 0.01, "setup", "before run")}}
}

输出webpack构建进度的

compiler.hooks.run.call

在compiler.hooks.beforeRun的回调中触发hooks.run钩子,webpack内部暂时无法插件订阅该钩子

compiler.readRecords方法

//代码简化
class Compiler {readRecords(callback) {if (this.hooks.readRecords.isUsed()) {if (this.recordsInputPath) {asyncLib.parallel([cb => this.hooks.readRecords.callAsync(cb),this._readRecords.bind(this)]);} else {this.records = {};this.hooks.readRecords.callAsync(callback);}} else {if (this.recordsInputPath) {this._readRecords(callback);} else {this.records = {};callback();}}}}

首先判断是否注册了compiler.hooks.readRecords钩子,有则判断是否有recordsInputPath配置,有则触发this.hooks.readRecords,会触发使用records的相关插件执行。然后触发this._readRecords.bind(this)
this._readRecords

class Compielr {//...._readRecords(callback) {//路径不存在if (!this.recordsInputPath) {this.records = {};return callback();}// 路径存在this.inputFileSystem.stat(this.recordsInputPath, err => {this.inputFileSystem.readFile(this.recordsInputPath, (err, content) => {this.records = parseJson(content.toString("utf-8"));return callback();});});}
}

onCompiled函数

上面的函数处理的是beforeCompile到afterCompile钩子,onCompiled函数则处理后续的编译产物和records的写入工作。

const onCompiled = (err, compilation) => {// err对象,编译失败,调用finalCallback这个函数终止编译if (err) return finalCallback(err);// 触发this.hooks.shouldEmit.call这个钩子。// shouldEmit标识是否输出编译产物,如果不想让产物输出,可以订阅这个钩子,并在这个钩子最后返回false,这样可以防止产物写入本地文件系统。if (this.hooks.shouldEmit.call(compilation) === false) {// 阻止文件写入compilation.startTime = startTime;compilation.endTime = Date.now();const stats = new Stats(compilation);this.hooks.done.callAsync(stats, err => {if (err) return finalCallback(err);return finalCallback(null, stats);});return;}process.nextTick(() => {// 写入文件this.emitAssets(compilation, err => {if (compilation.hooks.needAdditionalPass.call()) {// 暂时忽略}// 完成写入工作this.emitRecords(err => {compilation.startTime = startTime;compilation.endTime = Date.now();const stats = new Stats(compilation);this.hooks.done.callAsync(stats, err => {this.cache.storeBuildDependencies(compilation.buildDependencies,err => {// records完成后调用finalCallback函数完成最终的收尾工作return finalCallback(null, stats);});});});});});
};

finalCallback函数
用于处理compiler.run方法的收尾工作

const finalCallback = (err, stats) => {// idle是处理持久化缓存的标识this.idle = true;this.cache.beginIdle();this.idle = true;// 关闭本编译器this.running = false;if (err) {this.hooks.failed.call(err);}// callback是webpack-cli里面启动编译器传入的,这里算是webpack编译器和webpack-cli在通信。if (callback !== undefined) callback(err, stats);// 触发compiler.hooks.afterDone钩子,告知订阅插件做收编译工作的总结。this.hooks.afterDone.call(stats);
};

调用compiler.cache.benginIdle()方法, 标识编译器空闲,可以进行缓存写入工作。后续在IdleFileCachePlugin和PackFileCacheStrategy完成

webpack流程

webpack的运行是一个串行的过程

  1. 初始化: 启动构建,读取以及合并参数,加载plugin,实例化compiler
  2. 编译,从entry发出,针对module串行调用对应的loader去翻译文件内容,然后找到module依赖的module,递归的进行编译处理
  3. 输出: 对编译后的module组合称为chunk,把chunk转换为文件,输出到文件系统
    未开启监听模式不会有文件发生变化的那个箭头,开启监听模式为
    在这里插入图片描述

初始化阶段

合并shell和配置文件的参数形成实例化complier对象 => 加载插件 => 处理入口

  • 初始化参数:从配置文件和shell语句中读取合并参数,得到最终的参数。还会执行配置文件中的插件实例化语句new plugin()
  • 实例化compiler: 从上一步的参数初始化compiler实例,compiler实例负责整个文件的监听和启动编译,compiler中包含了完整的webpack配置。全局中只有一个compiler实例对象
  • 加载插件: 依次调用插件的apply方法,让插件可以监听后续的所有事件节点,并且传入compiler实例的引用,方便插件可以通过compiler调用webpack提供的api
  • enviroment:开始应用node.js风格的文件系统到compiler对象,以方便后续的文件寻找和读取
  • entry-option:读取配置的entry是,为每个entry实例化一个对应的entryplugin,为后面的entry的递归解析做准备。
  • after-plugin: 调用完所有的内置和配置的插件的apply方法
  • after-resolvers: 根据配置初始化弯沉resolver,resolver负责在文件系统中寻找指定路径的文件

编译阶段

  • before-run: 清除缓存
  • run: 启动一次新的编译
  • watch-run: 和run类型,实在监视模式下启动的编译,这个事件中可以获得到哪些文件发生了变化导致重新启动一次新的编译
  • compile: 告诉插件一次新的编译要启动,会给插件带上compiler对象
  • compilation: 当webpack以开发模式运行的时候,当检测到文件变化的时候,一次新的compilation将会被创建,一个compilation对象包含了当前的模块资源,编译生成资源,变化的文件等,compilation对象也提供了很多事件回调供插件做拓展
  • make: 一个新的compilation创建完毕,就会从entry开始读取文件,根据文件类型和配置loader对文件进行编译,编译完成之后再找出对该文件依赖的文件,递归的进行编译和解析
  • after-compile: 一次compilation执行完成,这里会根据编译结果,合并出我们最终生成的文件名和文件内容
  • invalid: 当遇到文件不存在,文件编译错误等的异常情况下会触发这种事件,该事件不会导致webpack退出。
    compilation实际上就是调用相应的loader处理文件生成chunks并对这些chunks做优化的过程
  • build-module:使用对应的loader去转换一个模块
  • normal-module-loader: 使用loader对一个模块进行转换完之后,使用acorn转换后面的内容,输出对应的抽象语法树,方便以后webpack对后面的代码进行分析
  • program: 从配置的入口模块开始,分析其ast,当遇到require等导入其他模块的语句之后,将其加到依赖的模块列表中,同时找到新找出的依赖模块进行递归分析,最后搞清楚所有模块的依赖关系
  • seal: 所有模块及其依赖通过loader进行转换完成之后,根据依赖开始生成chunk

输出阶段

  • should-emit: 所有的输出文件已经生成好,询问插件那些文件需要输出,那些不需要
  • emit: 确定好那些文件后,执行文件输出,可以再这里获取和修改输出内容
  • after-emit: 文件输出完毕
  • done: 成功完成一次完成编译和输出流程
  • failed: 如果再编译和输出过程中遇到异常或者是导致webpack退出的时候,就会跳到此步骤,插件可以再本事件中获取到具体的错误原因。

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

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

相关文章

【最新】2024年全球汽车零部件供应商百强榜,15家中企上榜!

6月23日,《美国汽车新闻》(Automotive News)重磅发布了2024年全球汽车零部件供应商百强榜。来自中国的动力电池企业宁德时代挤进了前四,中国企业一共上榜15家,较去年多了两家。国轩高科和三花零件为新进榜单的中企。 …

WPF自定义控件,实现含有箭头的文本或内容控件

文章目录 背景效果预览方案设计分析基本布局添加控件自定义属性添加属性值监听获取点数据 全部代码HorizontalLineContent.xamlHorizontalLineContent.xaml.csDirectionAlignment.csContentDirectionAlignment.cs 使用方法 背景 因为项目开发需要,要在WPF上绘制TCP…

【neo4j图数据库】入门实践篇

探索数据之间的奥秘:Neo4j图数据库引领新纪元 在数字化浪潮汹涌的今天,数据已成为企业最宝贵的资产之一。然而,随着数据量的爆炸性增长和数据关系的日益复杂,传统的关系型数据库在处理诸如社交网络、推荐系统、生物信息学等高度互…

代码随想录算法训练营第四十一天| 322. 零钱兑换、279.完全平方数、139.单词拆分

322. 零钱兑换 题目链接:322. 零钱兑换 文档讲解:代码随想录 状态:能想到凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],但没想到加上一个钱币coins[i]即dp[j - coins[i]] 1就是dp[j](考虑coins[i]&#xff09…

IDEA 好用的插件,必备的插件

1. GitToolBox 菜单栏显示git分支信息 2.MyBatisx 快速定位找到sql的xml文件 3.RestfulToolkit-fix 快速定位接口的插件 默认快捷键: CtrlAltN 4.EasyCamelQSM 字符串转驼峰 默认快捷键: Ctrl Alt Q 5.Maven Helper 检查maven冲突,图形化展示maven依赖的插…

Conan安装与C++第三方环境配置保姆级图文教程(附速查字典)

目录 1 什么是Conan?2 Conan安装与配置3 Conan的常见操作3.1 搜索指定包3.2 安装指定包3.3 本地包管理3.4 查看项目依赖 4 Conan构建项目案例 1 什么是Conan? Conan是一个开源的C/C包管理器,用于管理和构建C/C项目所需的依赖库。传统上&…

【启明智显分享】2.8寸触摸串口屏SC05 Plus应用于智能血压计

2.8寸SC05 Plus串口触摸屏,带WIFI/蓝牙 我国高血压流行病调查显示,成人高血压患病率为27.9%、知晓率为46.9%、治疗率为40.7%、控制率为15.3%。由此可见高血压的患病率高,但知晓率和治疗率低&…

【微信小程序开发实战项目】——如何制作一个属于自己的花店微信小程序(1)

👨‍💻个人主页:开发者-曼亿点 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 曼亿点 原创 👨‍💻 收录于专栏&#xff1a…

高危行业的安全守护者,顶坚防爆手机无惧挑战

高危行业的安全守护者,防爆手机以卓越性能,无惧极端挑战,为每一位前线工作者筑起坚不可摧的安全防线。石油勘探的深邃海洋、化工生产的复杂车间、矿山的幽深隧道……这些高危行业中,每一步都需谨慎前行,每一刻都需安全…

基于springboot的校园商铺管理系统

功能结构图: 实现图: 后台功能: 商品管理 公告管理 前台页面 详情 订单 我的订单

SciencePub学术刊源 | 7月SCI/SSCI/EI/CNKI刊源表已更新!(内含TOP及CCF推荐)

【SciencePub学术】我处SciencePub学术2024年7月SCI/SSCI/EI/CNKI刊源表已更新!内含多本中科院TOP、CCF推荐以及进展超顺的优质期刊,最快1-3个月录用! 计算机领域重点SCI 环境地质化学领域重点SCI 生物医学领域重点SCI 数学材料领域重点SCI 各…

同星TTS系列产品全新亮相:让开发测试变得更简单!

TTS系列产品 如果需要完整地测试 ECU,不仅需要将通信网络连接到测试系统,还需要连接 I/O 接口。同星的TTS测试系统将连接 I/O 通道所需的所有电路组件集成在一个模块中,可以极大地简化测试台架和HIL测试系统的设置,提高搭建和测试…

武汉星起航:跨境电商领域的领航者,助力合作伙伴全球布局

在跨境电商的汹涌浪潮中,武汉星起航电子商务有限公司如同一颗璀璨的明星,自2017年起便以亚马逊自营店铺为核心业务,不断积累实战运营经验,逐步建立了自己在市场中的稳固地位。随着2020年公司的正式成立,武汉星起航明确…

学习笔记——动态路由——IS-IS中间系统到中间系统(背景)

一、IS-IS技术背景 1、前言 IS-IS最初是国际标准化组织ISO(the International Organization for Standardization)为它的无连接网络协议CLNP(ConnectionLess Network Protocol)设计的一种动态路由协议。 和OSPF一样,IS-IS也是一种基于链路状态并使用最短路径优先…

浅谈制造企业如何借力EHS,让安全管理上新台阶

当今商业环境中,企业管理不仅关注经济效益,更将目光投向了长远发展的基石——EHS(环境Environment、健康Health、安全Safety)管理体系。这一体系的崛起,标志着企业管理理念的一次深刻变革,它如同企业的守护…

Chisel学习笔记(1)——Chisel安装与Verilog代码仿真

参考链接: https://www.chisel-lang.org/docs/installation 使用Chisel语言编写硬件描述语言,相比于使用Verilog会更加地灵敏快捷,Coding效率更高,但似乎debug会出现一些小问题。但新工具还是要尝试一下才知道好不好用。 1 安装C…

构建RAG+nebula graph(知识图谱KG)

目标:通过利用 LlamaIndex 和 NebulaGraph 为费城费城人队(Philadelphia Phillies)构建一个RAG流程,深入探讨知识图谱。 NebulaGraph 是市场上最好的知识图谱数据库之一。它是开源的、分布式的,并且能够处理具有亿万边…

【linux】网络基础(2)——udp协议

文章目录 引言udp协议的特点udp的头部结构UDP的工作原理简单的UDP网络程序套接字的认识udp服务端代码udp客户端代码服务端运行 引言 用户数据报协议(User Datagram Protocol, UDP)是一种无连接的传输层协议。它是因特网协议家族的一部分,定义…

C语言的数据结构:图的基本概念

前言 之前学过了其它的数据结构,如: 集合 \color{#5ecffd}集合 集合 —— 数据元素属于一个集合。 线型结构 \color{#5ecffd}线型结构 线型结构 —— 一个对一个,如线性表、栈、队列,每一个节点和其它节点之间的关系 一个对一个…

超酷的打字机效果?分享 1 段优质 CSS 代码片段!

大家好,我是大澈! 本文约 900 字,整篇阅读约需 1 分钟。 每日分享一段优质代码片段。 今天分享一段优质 CSS 代码片段,实现打字机般的效果或其他类似的离散动画效果。 老规矩,先阅读代码片段并思考,再看代…