第 94 期 - Rollup 构建后的输出流程解析
摘要
文章阐述 Rollup 构建完成后的输出流程,包括 Module 排序、循环引用检测、Chunk 生成、内容生成以及输出生命周期,还提及目前内容只是宏观层面,后续将讲述 TreeShaking 等更多内容。
一、Rollup 构建后的整体流程
Rollup 构建完成后,内存中充满 Module 类实例,输出流程就是将这些数据写入磁盘完成前端工程构建。这其中涉及多个环节,包括 Module 的排序、与 Chunk 和 Bundle 类的交互、处理循环引用、生成 Chunk、生成内容以及输出生命周期等内容。
二、Module 排序原理
(一)排序的必要性
JS 引用有先后顺序,为避免访问未声明变量而报错,Module 需要排序。
(二)排序的方法
以拓扑排序为例,若有a.js->b.js->c.js
这样的依赖关系,无依赖的c.js
先输出,依赖c.js
的b.js
其次,依赖b.js
和c.js
的a.js
最后输出。
(三)Rollup 中的拓扑排序
- 在
analyseModuleExecution
函数内部定义analyseModule
函数用于递归调用。 - 第一个
for
语句依次调用analyseModule
函数,dependencies
存储当前 Module 依赖内容,做深度优先遍历,有定义记录引用的Map
(值为parents
),每处理一个 Module 记录引用关系。 nextExecIndex
是最外层变量,递归体在nextExecIndex ++
语句前,按之前例子最终c.js
的index
为 1,b.js
为 2,a.js
为 3,用于后续排序。
三、循环引用检测
(一)循环引用的危害
在实际开发中应尽量减少编写循环引用代码。
(二)Rollup 中的处理
- 当 Module 间有循环依赖且被打包进一个
Chunk
时没问题,但划分至多个Chunk
时,加载产物会报错。 - Rollup 处理循环引用的函数执行条件是已确定有循环依赖,其逻辑是典型循环链表探测环,
module
是头结点,不断向后迭代,当迭代到的节点为module
时读取到完整环,处理过程中还会往 Module 里增加节点信息。 - 对于
getCyclePath
函数,第一个if
条件成立时可能是已处理模块,当两个if
条件同时成立说明有大量 Module 节点无法完成拓扑排序,需记录依赖环。
四、生成 Chunk
(一)自动分包策略
在不配置自定义分包策略时,Rollup 利用位运算生成键,当键相同 Module 可划分到同一Chunk
。
(二)位运算原理
- 以
let chunkSignature = 0n; for (const entryIndex of dependentEntries) {chunkSignature |= 1n << BigInt(entryIndex);}
为例。 - 使用
BigInt
和位移操作创建位标记:1n << BigInt(entryIndex)
将初始值为 1 的大整数左移entryIndex
位,每个entryIndex
对应二进制位图中独特位置。 - 使用按位或叠加多个标志位:遍历
dependentEntries
时对chunkSignature
不断进行OR
运算,将出现在集合中的entryIndex
转化为在二进制数chunkSignature
中置位。 - 意义是将一组依赖入口索引压缩成唯一二进制标识,快速对具有相同依赖入口模式的模块分组。
(三)后续操作
Chunk
划分完成后要进行产物输出准备,将 Module 内容按之前确定的排序依据合并(打包)。
五、生成内容
(一)基本处理
- 先不考虑
TreeShaking
,调用每个Chunk
的render
函数处理 Module 内容生成。 - 以
magicString
为例,它是打包合并初加工产物但部分哈希码未确定,其来源是将AST
转化成生成目标进行输出。 - 可在生成内容的
header
和footer
插入自定义内容。
(二)路径处理
对粗加工打包代码进行精加工,把代码里引用的文件路径替换成带哈希码的真实相对路径。
六、输出生命周期
- 首先触发
outputOptions
,然后renderStart
生命周期。 - 根据 Module 信息划分
Chunk
,划分后为输出准备工作,触发banner
、footer
、intro
、outro
等生命周期可追加版权信息。 - 把文件内容合并成粗加工内容,触发
renderChunk
生命周期,根据用户配置得到哈希依据触发argumentChunkHash
生命周期,粗加工内容根据哈希码替换引用规则后触发generateBundle
生命周期。 - 内容准备好后写入磁盘,触发
writeBundle
生命周期,最后closeBundle
生命周期。 - 所有逻辑在
try - catch
中执行,遇到错误执行renderError
生命周期。部分生命周期如renderDynamicImport
、resolveFileUrl
、resolveImportMeta
未在此分析。
扩展阅读
Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有