Appearance
ModuleGraph 架构分析
整体架构
ModuleGraph 使用图数据结构表示模块之间的依赖关系。
┌─────────────────────────────────────────────┐
│ ModuleGraph │
├─────────────────────────────────────────────┤
│ 1. 模块节点(ModuleNode) │
│ - id: 模块标识符 │
│ - url: 模块 URL │
│ - file: 模块文件路径 │
│ - imports: 导入的模块 │
│ - importers: 导入此模块的模块 │
│ - transformResult: 转换结果 │
├─────────────────────────────────────────────┤
│ 2. 模块映射(ModuleMap) │
│ - idToModuleMap: ID 到模块的映射 │
│ - urlToModuleMap: URL 到模块的映射 │
│ - fileToModulesMap: 文件到模块的映射 │
├─────────────────────────────────────────────┤
│ 3. 入口模块(EntryModules) │
│ - Set<ModuleNode>: 入口模块集合 │
└─────────────────────────────────────────────┘核心组件
1. ModuleNode
模块节点表示单个模块。
属性:
typescript
class ModuleNode {
id: string // 模块 ID
url: string // 模块 URL
file: string // 模块文件路径
imports: Set<ModuleNode> // 导入的模块
importers: Set<ModuleNode> // 导入此模块的模块
transformResult: TransformResult | null // 转换结果
lastHMRTimestamp: number // 最后 HMR 时间戳
meta: Record<string, any> // 元数据
}方法:
typescript
// 添加导入
addImport(module: ModuleNode): void {
this.imports.add(module)
module.importers.add(this)
}
// 移除导入
removeImport(module: ModuleNode): void {
this.imports.delete(module)
module.importers.delete(this)
}
// 获取所有导入
getImports(): ModuleNode[] {
return Array.from(this.imports)
}
// 获取所有导入者
getImporters(): ModuleNode[] {
return Array.from(this.importers)
}2. ModuleGraph
模块图管理所有模块和依赖关系。
属性:
typescript
class ModuleGraph {
idToModuleMap: Map<string, ModuleNode> // ID 到模块的映射
urlToModuleMap: Map<string, ModuleNode> // URL 到模块的映射
fileToModulesMap: Map<string, Set<ModuleNode>> // 文件到模块的映射
entryModules: Set<ModuleNode> // 入口模块集合
}方法:
typescript
// 创建模块
createModule(options: ModuleOptions): ModuleNode {
const module = new ModuleNode(options)
this.addModule(module)
return module
}
// 添加模块
addModule(module: ModuleNode): void {
this.idToModuleMap.set(module.id, module)
this.urlToModuleMap.set(module.url, module)
if (module.file) {
if (!this.fileToModulesMap.has(module.file)) {
this.fileToModulesMap.set(module.file, new Set())
}
this.fileToModulesMap.get(module.file)!.add(module)
}
}
// 获取模块
getModuleById(id: string): ModuleNode | undefined {
return this.idToModuleMap.get(id)
}
getModuleByUrl(url: string): ModuleNode | undefined {
return this.urlToModuleMap.get(url)
}
getModulesByFile(file: string): Set<ModuleNode> | undefined {
return this.fileToModulesMap.get(file)
}
// 更新模块
updateModule(module: ModuleNode): void {
// 更新模块信息
}
// 删除模块
deleteModule(module: ModuleNode): void {
this.idToModuleMap.delete(module.id)
this.urlToModuleMap.delete(module.url)
if (module.file) {
this.fileToModulesMap.get(module.file)?.delete(module)
}
// 清理依赖关系
for (const imp of module.imports) {
imp.importers.delete(module)
}
for (const imp of module.importers) {
imp.imports.delete(module)
}
}依赖关系
导入关系
Module A
↓ imports
Module B
↓ imports
Module C实现:
typescript
// A 导入 B
moduleA.addImport(moduleB)
// B 导入 C
moduleB.addImport(moduleC)双向关系
Module A ← importers ← Module B ← importers ← Module C
↓ imports ↓ imports
Module B Module C实现:
typescript
// 查找导入者
const importers = moduleA.getImporters() // []
// 查找导入
const imports = moduleA.getImports() // [ModuleB]HMR 支持
模块更新
当文件变化时,更新模块:
typescript
async function invalidateModule(file: string) {
const modules = moduleGraph.getModulesByFile(file)
if (modules) {
for (const module of modules) {
// 清除转换结果
module.transformResult = null
// 通知 HMR 客户端
hmrClient.send({
type: 'update',
path: module.url
})
}
}
}依赖更新
更新受影响的模块:
typescript
async function updateDependents(module: ModuleNode) {
const dependents = module.getImporters()
for (const dependent of dependents) {
// 重新转换
dependent.transformResult = await transform(dependent)
// 递归更新
await updateDependents(dependent)
}
}循环检测
检测算法
使用深度优先搜索(DFS)检测循环:
typescript
function detectCycle(module: ModuleNode, visited = new Set()): boolean {
if (visited.has(module)) {
return true // 检测到循环
}
visited.add(module)
for (const imp of module.imports) {
if (detectCycle(imp, new Set(visited))) {
return true
}
}
return false
}循环处理
检测到循环时,记录警告:
typescript
if (detectCycle(module)) {
console.warn(`检测到循环依赖: ${module.id}`)
}性能优化
1. 使用 Map 和 Set
typescript
// 快速查找
const module = idToModuleMap.get(id)
// 快速判断
if (imports.has(module)) {
// 已存在
}2. 增量更新
typescript
// 只更新变化的模块
for (const file of changedFiles) {
const modules = getModulesByFile(file)
for (const module of modules) {
updateModule(module)
}
}3. 缓存策略
typescript
// 缓存转换结果
if (!module.transformResult) {
module.transformResult = await transform(module)
}总结
ModuleGraph 的架构体现了以下特点:
- 图结构:使用图表示依赖关系
- 双向关系:维护导入和导入者关系
- 高效查询:使用 Map 和 Set 实现快速查找
- HMR 支持:支持热模块替换
理解 ModuleGraph 的架构有助于更好地使用和优化构建工具。
参考资源

扫描二维码关注"架构师AI杜"公众号,获取更多技术内容和最新动态
