Appearance
ModuleGraph 源代码导览
项目结构
modulegraph-course/
├── 04-core-feature/ # 核心功能实现
│ ├── src/
│ │ └── graph.js # 模块图实现
│ ├── test/
│ │ └── graph.test.js # 测试文件
│ ├── package.json
│ └── README.md
├── 05-lesson-plan.md # 课程计划
├── 01-intro.md # 背景研究
├── 02-arch.md # 架构分析
└── 03-code-walkthrough.md # 源代码导览核心文件解析
1. graph.js - 模块图
文件路径: src/graph.js
核心功能:
- 管理模块节点
- 维护依赖关系
- 支持模块查询和更新
关键代码:
javascript
// 模块节点
export class ModuleNode {
constructor(options = {}) {
this.id = options.id
this.url = options.url
this.file = options.file
// 依赖
this.imports = new Set()
this.importers = new Set()
// 状态
this.transformResult = null
this.lastHMRTimestamp = 0
// 元数据
this.meta = options.meta || {}
}
// 添加导入
addImport(module) {
this.imports.add(module)
module.importers.add(this)
}
// 移除导入
removeImport(module) {
this.imports.delete(module)
module.importers.delete(this)
}
// 获取所有导入
getImports() {
return Array.from(this.imports)
}
// 获取所有导入者
getImporters() {
return Array.from(this.importers)
}
// 更新转换结果
updateTransformResult(result) {
this.transformResult = result
this.lastHMRTimestamp = Date.now()
}
}
// 模块图
export class ModuleGraph {
constructor() {
// 模块映射
this.idToModuleMap = new Map()
this.urlToModuleMap = new Map()
this.fileToModulesMap = new Map()
// 入口模块
this.entryModules = new Set()
}
// 创建模块
createModule(options) {
const module = new ModuleNode(options)
this.addModule(module)
return module
}
// 添加模块
addModule(module) {
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) {
return this.idToModuleMap.get(id)
}
getModuleByUrl(url) {
return this.urlToModuleMap.get(url)
}
getModulesByFile(file) {
return this.fileToModulesMap.get(file)
}
// 更新模块
updateModule(module) {
// 更新模块信息
}
// 删除模块
deleteModule(module) {
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)
}
}
// 添加入口模块
addEntryModule(module) {
this.entryModules.add(module)
}
// 获取入口模块
getEntryModules() {
return Array.from(this.entryModules)
}
}设计要点:
- 使用 Map 实现快速查找
- 使用 Set 存储依赖关系
- 维护双向依赖关系
- 支持文件到模块的多对一映射
关键设计决策
1. 使用 Map 和 Set
原因:
- Map 提供 O(1) 的查找性能
- Set 自动去重
- 内存效率高
实现:
javascript
this.idToModuleMap = new Map()
this.imports = new Set()2. 双向依赖关系
原因:
- 便于查找导入者
- 支持 HMR 的依赖更新
- 便于循环检测
实现:
javascript
addImport(module) {
this.imports.add(module)
module.importers.add(this) // 双向关系
}3. 文件到模块的多对一映射
原因:
- 同一文件可能对应多个模块(如 HMR)
- 便于按文件查找模块
- 支持虚拟模块
实现:
javascript
this.fileToModulesMap = new Map()
this.fileToModulesMap.set(file, new Set())
this.fileToModulesMap.get(file).add(module)测试策略
单元测试
javascript
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { ModuleGraph, ModuleNode } from '../src/graph.js'
describe('ModuleGraph 测试', () => {
it('应该创建模块图', () => {
const graph = new ModuleGraph()
assert.ok(graph)
})
it('应该创建模块', () => {
const graph = new ModuleGraph()
const module = graph.createModule({
id: 'test',
url: '/test.js'
})
assert.ok(module)
assert.strictEqual(module.id, 'test')
})
it('应该添加依赖', () => {
const moduleA = new ModuleNode({ id: 'a' })
const moduleB = new ModuleNode({ id: 'b' })
moduleA.addImport(moduleB)
assert.ok(moduleA.imports.has(moduleB))
assert.ok(moduleB.importers.has(moduleA))
})
})性能优化
1. 模块缓存
javascript
const moduleCache = new Map()
function getModule(id) {
if (moduleCache.has(id)) {
return moduleCache.get(id)
}
const module = createModule(id)
moduleCache.set(id, module)
return module
}2. 增量更新
javascript
function updateModules(changedFiles) {
for (const file of changedFiles) {
const modules = graph.getModulesByFile(file)
for (const module of modules) {
updateModule(module)
}
}
}总结
ModuleGraph 的源代码体现了以下设计原则:
- 高效查询:使用 Map 实现 O(1) 查找
- 双向关系:维护导入和导入者关系
- 灵活映射:支持多种查询方式
- 易于测试:提供清晰的接口
理解源代码有助于更好地使用和优化 ModuleGraph。
参考资源

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