Appearance
Rollup 架构分析
整体架构
Rollup 采用基于 AST 和插件系统的架构,通过解析、转换和生成三个阶段打包代码。
┌─────────────────────────────────────────────┐
│ Rollup Bundler │
├─────────────────────────────────────────────┤
│ 1. Parser (解析器) │
│ - Acorn Parser (Acorn 解析器) │
│ - AST Generation (AST 生成) │
│ - Module Resolution (模块解析) │
├─────────────────────────────────────────────┤
│ 2. Transform (转换器) │
│ - Plugin System (插件系统) │
│ - Tree-shaking (树摇) │
│ - Code Splitting (代码分割) │
├─────────────────────────────────────────────┤
│ 3. Generator (生成器) │
│ - Code Generation (代码生成) │
│ - Source Map (源映射) │
│ - Output Formats (输出格式) │
└─────────────────────────────────────────────┘核心组件
1. Parser
解析器负责解析模块和生成 AST。
职责:
- 解析模块
- 生成 AST
- 解析依赖
关键方法:
javascript
class Parser {
constructor(options) {
this.options = options
this.acornOptions = {
sourceType: 'module',
ecmaVersion: 'latest',
...options.acorn
}
}
// 解析模块
parse(source, id) {
const ast = acorn.parse(source, this.acornOptions)
const dependencies = this.extractDependencies(ast)
return {
id,
ast,
dependencies,
source
}
}
// 提取依赖
extractDependencies(ast) {
const dependencies = []
traverse(ast, {
ImportDeclaration(path) {
dependencies.push(path.node.source.value)
},
ExportNamedDeclaration(path) {
if (path.node.source) {
dependencies.push(path.node.source.value)
}
},
ExportAllDeclaration(path) {
dependencies.push(path.node.source.value)
}
})
return dependencies
}
}优势:
- 准确:准确的 AST 生成
- 快速:快速的解析速度
- 完整:支持完整的 ES6 语法
2. Transform
转换器负责应用插件和转换代码。
职责:
- 应用插件
- Tree-shaking
- 代码分割
关键方法:
javascript
class Transform {
constructor(options) {
this.options = options
this.plugins = options.plugins || []
}
// 转换模块
async transform(module) {
const context = {
id: module.id,
code: module.source,
ast: module.ast
}
// 应用插件
for (const plugin of this.plugins) {
if (plugin.transform) {
const result = await plugin.transform.call(context, context.code, context.id)
if (result) {
context.code = result.code || context.code
context.ast = result.ast || context.ast
}
}
}
// Tree-shaking
const usedExports = this.analyzeUsedExports(context.ast)
context.ast = this.pruneUnusedExports(context.ast, usedExports)
return context
}
// 分析使用的导出
analyzeUsedExports(ast) {
const usedExports = new Set()
traverse(ast, {
Identifier(path) {
if (path.isReferencedIdentifier()) {
usedExports.add(path.node.name)
}
}
})
return usedExports
}
// 移除未使用的导出
pruneUnusedExports(ast, usedExports) {
// 移除未使用的导出
return ast
}
}优势:
- 插件化:支持插件扩展
- Tree-shaking:移除未使用的代码
- 代码分割:支持代码分割
3. Generator
生成器负责生成最终代码。
职责:
- 生成代码
- 生成 Source Map
- 支持多种输出格式
关键方法:
javascript
class Generator {
constructor(options) {
this.options = options
this.format = options.format || 'es'
}
// 生成代码
generate(modules) {
const chunks = this.createChunks(modules)
const output = []
for (const chunk of chunks) {
const code = this.generateChunk(chunk)
output.push({
code,
fileName: chunk.fileName
})
}
return output
}
// 创建代码块
createChunks(modules) {
const entryPoints = modules.filter(m => m.isEntry)
const chunks = []
for (const entry of entryPoints) {
const chunk = {
id: entry.id,
modules: [entry],
fileName: this.getFileName(entry.id)
}
// 添加依赖模块
this.addDependencies(chunk, entry, modules)
chunks.push(chunk)
}
return chunks
}
// 添加依赖
addDependencies(chunk, module, modules) {
for (const depId of module.dependencies) {
const dep = modules.find(m => m.id === depId)
if (dep && !chunk.modules.includes(dep)) {
chunk.modules.push(dep)
this.addDependencies(chunk, dep, modules)
}
}
}
// 生成代码块
generateChunk(chunk) {
let code = ''
for (const module of chunk.modules) {
code += this.generateModule(module)
}
return code
}
// 生成模块
generateModule(module) {
const magicString = new MagicString(module.source)
// 移除导入导出语句
this.removeImports(module.ast, magicString)
this.removeExports(module.ast, magicString)
return magicString.toString()
}
// 移除导入
removeImports(ast, magicString) {
traverse(ast, {
ImportDeclaration(path) {
magicString.remove(path.node.start, path.node.end)
}
})
}
// 移除导出
removeExports(ast, magicString) {
traverse(ast, {
ExportNamedDeclaration(path) {
magicString.remove(path.node.start, path.node.end)
},
ExportDefaultDeclaration(path) {
magicString.remove(path.node.start, path.node.end)
},
ExportAllDeclaration(path) {
magicString.remove(path.node.start, path.node.end)
}
})
}
// 获取文件名
getFileName(id) {
const base = path.basename(id, path.extname(id))
return `${base}.js`
}
}优势:
- 灵活:支持多种输出格式
- 高效:高效的代码生成
- 准确:准确的代码生成
插件系统
插件结构
javascript
export default function myPlugin(options = {}) {
return {
name: 'my-plugin',
// 解析钩子
resolveId(source, importer) {
// 解析模块 ID
},
load(id) {
// 加载模块
},
transform(code, id) {
// 转换代码
},
// 生成钩子
renderChunk(code, chunk) {
// 转换代码块
},
generateBundle(options, bundle) {
// 生成包
}
}
}插件执行
Entry File
↓
resolveId
↓
load
↓
transform
↓
generate
↓
Output FilesTree-shaking
原理
Tree-shaking 通过分析代码的使用情况,移除未使用的代码。
实现
javascript
function treeShake(ast) {
// 分析使用的导出
const usedExports = analyzeUsedExports(ast)
// 移除未使用的导出
return pruneUnusedExports(ast, usedExports)
}
function analyzeUsedExports(ast) {
const usedExports = new Set()
traverse(ast, {
Identifier(path) {
if (path.isReferencedIdentifier()) {
usedExports.add(path.node.name)
}
}
})
return usedExports
}代码分割
原理
代码分割将代码分割成多个块,按需加载。
实现
javascript
function codeSplit(modules) {
const entryPoints = modules.filter(m => m.isEntry)
const chunks = []
for (const entry of entryPoints) {
const chunk = createChunk(entry)
chunks.push(chunk)
}
return chunks
}性能优化
1. 增量构建
javascript
function buildIncremental(changedFiles) {
const affected = getAffectedModules(changedFiles)
for (const module of affected) {
rebuildModule(module)
}
}2. 并行处理
javascript
async function buildParallel(modules) {
const chunks = chunkArray(modules, os.cpus().length)
const promises = chunks.map(chunk =>
Promise.all(chunk.map(m => buildModule(m)))
)
return Promise.all(promises)
}3. 缓存
javascript
const cache = new Map()
function build(module) {
if (cache.has(module.id)) {
return cache.get(module.id)
}
const result = buildInternal(module)
cache.set(module.id, result)
return result
}总结
Rollup 的架构体现了以下特点:
- ES6 优先:专注于 ES6 模块
- Tree-shaking:移除未使用的代码
- 插件化:支持插件扩展
- 高效:高效的打包性能
理解 Rollup 的架构有助于更好地使用和优化 Rollup。
参考资源

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