一、入口点分割策略
入口文件会生成独立的 Chunk,由 EntryPlugin
触发。
源码解析
-
EntryPlugin 注册
EntryPlugin
在apply
方法中注册入口点:javascript// lib/EntryPlugin.js apply(compiler) { compiler.hooks.compilation.tap("EntryPlugin", (compilation, { normalModuleFactory }) => { compilation.dependencyFactories.set(EntryDependency, normalModuleFactory); }); compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => { const { entry, options, context } = this; const dep = EntryPlugin.createDependency(entry, options); compilation.addEntry(context, dep, options, err => { /* ... */ }); }); }
-
Chunk 创建
compilation.addEntry
调用addEntry
方法,最终在compilation
中创建 Chunk:javascript// lib/Compilation.js addEntry(context, entry, options, callback) { this._addEntryItem(context, entry, "dependencies", options, (err, module) => { if (module) { const chunk = this.addChunk(module.name); // 创建新 Chunk chunk.entryModule = module; chunk.name = module.name; // 将模块与 Chunk 关联 this.chunkGraph.connectChunkAndModule(chunk, module); } callback(err); }); }
关键点 :每个入口文件生成独立 Chunk,addChunk
是 Chunk 创建的源头。
二、动态导入分割策略
动态导入(import()
)通过 ImportDependenciesBlock
触发代码分割。
源码解析
-
依赖解析
ImportParserPlugin
处理import()
语法,生成ImportDependency
:javascript// lib/dependencies/ImportParserPlugin.js parser.hooks.importCall.tap("ImportParserPlugin", expr => { const dep = new ImportDependency(/* ... */); parser.state.current.addBlock(dep); // 添加到当前模块依赖 });
-
Chunk 分割
Compilation
处理依赖时触发分割:javascript// lib/Compilation.js handleModuleCreation(/* ... */) { this.processModuleDependencies(module, err => { // 处理 ImportDependency if (block instanceof ImportDependenciesBlock) { this.handleBlockPreOrder(block, (err, result) => { this.addChunkInGroup(block.group, module); // 创建新 Chunk Group }); } }); }
-
Chunk 生成
addChunkInGroup
最终生成新 Chunk:javascript// lib/Compilation.js addChunkInGroup(group, module) { const chunk = this.addChunk(module.identifier()); group.chunks.push(chunk); return chunk; }
关键点 :动态导入生成 ImportDependenciesBlock
,触发异步 Chunk 创建。
三、SplitChunksPlugin 优化策略
SplitChunksPlugin
通过模块复用与规则匹配优化 Chunk 结构。
源码解析
-
插件入口
SplitChunksPlugin
在optimizeChunks
阶段执行:javascript// lib/optimize/SplitChunksPlugin.js apply(compiler) { compiler.hooks.thisCompilation.tap("SplitChunksPlugin", compilation => { compilation.hooks.optimizeChunks.tap("SplitChunksPlugin", chunks => { this.optimize(compilation, chunks); }); }); }
-
模块分组
getCacheGroups
方法收集模块分组规则:javascriptgetCacheGroups(compilation) { const cacheGroups = []; for (const key of Object.keys(this.options.cacheGroups)) { const group = this.options.cacheGroups[key]; cacheGroups.push({ key, /* ...规则配置 */ }); } return cacheGroups; }
-
Chunk 分割逻辑
optimize
方法遍历模块,应用规则合并 Chunk:javascriptoptimize(compilation, chunks) { const cacheGroups = this.getCacheGroups(compilation); for (const module of compilation.modules) { for (const cacheGroup of cacheGroups) { if (cacheGroup.test(module)) { // 匹配模块规则 const chunk = this.getChunk(module, cacheGroup); if (!chunk) { chunk = compilation.addChunk(cacheGroup.name); // 创建新 Chunk } compilation.chunkGraph.connectChunkAndModule(chunk, module); } } } }
关键配置规则:
minSize
: 新 Chunk 最小体积minChunks
: 模块被引用次数阈值cacheGroups
: 自定义分组规则
四、Chunk 生成与优化流程图
graph TD
A[入口点] --> |addEntry| B[创建入口 Chunk]
C[动态导入] --> |ImportDependency| D[创建异步 Chunk]
E[SplitChunksPlugin] --> |缓存组规则| F[合并复用模块生成新 Chunk]
B --> G[优化阶段]
D --> G
G --> F
总结
- 入口点 :直接通过
addEntry
生成独立 Chunk。 - 动态导入 :通过
ImportDependenciesBlock
触发异步 Chunk。 - SplitChunksPlugin:基于模块复用规则优化 Chunk 结构。
源码中核心方法:
compilation.addChunk()
:Chunk 创建入口。chunkGraph.connectChunkAndModule()
:关联模块与 Chunk。SplitChunksPlugin.optimize()
:执行优化策略。
理解 Chunk 分割策略需要结合 模块依赖图 和 Chunk 合并规则,Webpack 通过分层处理(入口、动态加载、优化)实现灵活的代码分割。