Appearance
Monorepo 源代码导览
项目结构
monorepo-course/
├── 04-core-feature/ # 核心功能实现
│ ├── src/
│ │ ├── workspace.js # 工作空间管理
│ │ ├── graph.js # 依赖图
│ │ └── build.js # 构建系统
│ ├── test/
│ │ ├── workspace.test.js
│ │ ├── graph.test.js
│ │ └── build.test.js
│ ├── package.json
│ └── README.md
├── 05-lesson-plan.md # 课程计划
├── 01-intro.md # 背景研究
├── 02-arch.md # 架构分析
└── 03-code-walkthrough.md # 源代码导览核心文件解析
1. workspace.js - 工作空间管理
文件路径: src/workspace.js
核心功能: 管理工作空间,解析依赖关系,协调构建流程。
关键代码:
javascript
// 工作空间
export class Workspace {
constructor(options = {}) {
this.name = options.name
this.path = options.path
this.version = options.version || '1.0.0'
this.dependencies = options.dependencies || {}
this.devDependencies = options.devDependencies || {}
this.peerDependencies = options.peerDependencies || {}
}
// 获取所有依赖
getAllDependencies() {
return {
...this.dependencies,
...this.devDependencies,
...this.peerDependencies
}
}
// 检查是否依赖某个包
hasDependency(name) {
return this.getAllDependencies().hasOwnProperty(name)
}
}
// 工作空间管理器
export class WorkspaceManager {
constructor(config = {}) {
this.config = config
this.workspaces = new Map()
}
// 添加工作空间
addWorkspace(workspace) {
this.workspaces.set(workspace.name, workspace)
}
// 获取工作空间
getWorkspace(name) {
return this.workspaces.get(name)
}
// 获取所有工作空间
getWorkspaces() {
return Array.from(this.workspaces.values())
}
// 解析依赖关系
resolveDependencies() {
const graph = new DependencyGraph()
for (const workspace of this.workspaces.values()) {
graph.addNode(workspace.name, workspace)
for (const dep of Object.keys(workspace.getAllDependencies())) {
if (this.workspaces.has(dep)) {
graph.addEdge(workspace.name, dep)
}
}
}
return graph
}
}设计要点: 使用 Map 存储工作空间,支持多种依赖类型,自动解析依赖关系。
2. graph.js - 依赖图
文件路径: src/graph.js
核心功能: 构建依赖图,检测循环依赖,计算构建顺序。
关键代码:
javascript
// 依赖图
export class DependencyGraph {
constructor() {
this.nodes = new Map()
this.edges = new Map()
}
// 添加节点
addNode(name, workspace) {
this.nodes.set(name, workspace)
this.edges.set(name, new Set())
}
// 添加边
addEdge(from, to) {
if (this.edges.has(from)) {
this.edges.get(from).add(to)
}
}
// 获取依赖
getDependencies(name) {
return Array.from(this.edges.get(name) || [])
}
// 获取被依赖
getDependents(name) {
const dependents = []
for (const [node, deps] of this.edges.entries()) {
if (deps.has(name)) {
dependents.push(node)
}
}
return dependents
}
// 拓扑排序
topologicalSort() {
const visited = new Set()
const result = []
const visit = (name) => {
if (visited.has(name)) return
visited.add(name)
for (const dep of this.getDependencies(name)) {
visit(dep)
}
result.push(name)
}
for (const name of this.nodes.keys()) {
visit(name)
}
return result.reverse()
}
// 检测循环依赖
detectCycle() {
const visited = new Set()
const recursionStack = new Set()
const hasCycle = (name) => {
if (recursionStack.has(name)) {
return true
}
if (visited.has(name)) {
return false
}
visited.add(name)
recursionStack.add(name)
for (const dep of this.getDependencies(name)) {
if (hasCycle(dep)) {
return true
}
}
recursionStack.delete(name)
return false
}
for (const name of this.nodes.keys()) {
if (hasCycle(name)) {
return true
}
}
return false
}
}设计要点: 使用 Map 和 Set 存储图结构,支持拓扑排序,支持循环依赖检测。
3. build.js - 构建系统
文件路径: src/build.js
核心功能: 并行构建,增量构建,缓存构建结果。
关键代码:
javascript
// 构建缓存
export class BuildCache {
constructor() {
this.cache = new Map()
}
get(key) {
return this.cache.get(key)
}
set(key, value) {
this.cache.set(key, value)
}
has(key) {
return this.cache.has(key)
}
clear() {
this.cache.clear()
}
}
// 构建系统
export class BuildSystem {
constructor(config = {}) {
this.config = config
this.cache = new BuildCache()
}
// 获取缓存键
getCacheKey(workspace) {
return `${workspace.name}:${workspace.version}:${JSON.stringify(workspace.getAllDependencies())}`
}
// 构建工作空间
async buildWorkspace(workspace) {
const cacheKey = this.getCacheKey(workspace)
if (this.cache.has(cacheKey)) {
console.log(`使用缓存: ${workspace.name}`)
return this.cache.get(cacheKey)
}
console.log(`构建: ${workspace.name}`)
const result = await this.buildInternal(workspace)
this.cache.set(cacheKey, result)
return result
}
// 内部构建
async buildInternal(workspace) {
// 模拟构建过程
await new Promise(resolve => setTimeout(resolve, 100))
return {
name: workspace.name,
success: true,
timestamp: Date.now()
}
}
// 构建所有工作空间(按拓扑顺序)
async buildAll(workspaces) {
const graph = new DependencyGraph()
for (const workspace of workspaces) {
graph.addNode(workspace.name, workspace)
for (const dep of Object.keys(workspace.getAllDependencies())) {
if (workspaces.find(ws => ws.name === dep)) {
graph.addEdge(workspace.name, dep)
}
}
}
const order = graph.topologicalSort()
const results = []
for (const name of order) {
const workspace = graph.nodes.get(name)
const result = await this.buildWorkspace(workspace)
results.push(result)
}
return results
}
// 并行构建
async buildParallel(workspaces) {
const promises = workspaces.map(ws =>
this.buildWorkspace(ws)
)
return Promise.all(promises)
}
// 增量构建
async buildIncremental(workspaces, changedFiles) {
const affected = this.getAffectedWorkspaces(workspaces, changedFiles)
console.log(`受影响的工作空间: ${affected.map(ws => ws.name).join(', ')}`)
const results = []
for (const workspace of affected) {
const result = await this.buildWorkspace(workspace)
results.push(result)
}
return results
}
// 获取受影响的工作空间
getAffectedWorkspaces(workspaces, changedFiles) {
const affected = new Set()
for (const workspace of workspaces) {
for (const file of changedFiles) {
if (file.startsWith(workspace.path)) {
affected.add(workspace)
break
}
}
}
// 添加依赖这些工作空间的工作空间
const graph = new DependencyGraph()
for (const workspace of workspaces) {
graph.addNode(workspace.name, workspace)
for (const dep of Object.keys(workspace.getAllDependencies())) {
if (workspaces.find(ws => ws.name === dep)) {
graph.addEdge(workspace.name, dep)
}
}
}
const moreAffected = new Set()
for (const workspace of affected) {
const dependents = graph.getDependents(workspace.name)
for (const dep of dependents) {
moreAffected.add(graph.nodes.get(dep))
}
}
return [...affected, ...moreAffected]
}
}设计要点: 支持缓存构建结果,支持按拓扑顺序构建,支持并行和增量构建。
关键设计决策
1. 使用 Map 和 Set
原因: 快速查找,自动去重,内存效率高。
实现:
javascript
this.workspaces = new Map()
this.edges = new Map()2. 拓扑排序
原因: 确保正确的构建顺序,避免依赖问题,支持增量构建。
实现:
javascript
topologicalSort() {
const visited = new Set()
const result = []
const visit = (name) => {
if (visited.has(name)) return
visited.add(name)
for (const dep of this.getDependencies(name)) {
visit(dep)
}
result.push(name)
}
// ...
}3. 缓存构建结果
原因: 避免重复构建,提升构建速度,节省资源。
实现:
javascript
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey)
}
const result = await this.buildInternal(workspace)
this.cache.set(cacheKey, result)测试策略
单元测试
javascript
import { describe, it } from 'node:test'
import assert from 'node:assert'
import { Workspace, WorkspaceManager } from '../src/workspace.js'
describe('Workspace 测试', () => {
it('应该创建工作空间', () => {
const workspace = new Workspace({
name: 'test',
path: '/test'
})
assert.ok(workspace)
})
it('应该添加工作空间', () => {
const manager = new WorkspaceManager()
const workspace = new Workspace({
name: 'test',
path: '/test'
})
manager.addWorkspace(workspace)
assert.strictEqual(manager.getWorkspace('test'), workspace)
})
})总结
Monorepo 的源代码体现了四个核心设计原则:高效管理使用 Map 和 Set 实现高效查找,性能优异;依赖管理使用依赖图管理包之间的依赖关系,确保正确的构建顺序;构建优化支持缓存、并行和增量构建,提高构建效率;易于测试提供清晰的接口,便于单元测试和集成测试。理解源代码有助于更好地使用和优化 Monorepo。
参考资源

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