# Webpack 源码解析
# 源码调试(Vscode)
- 点击
运行和调试
- 选择
Node.js
- 在项目中
webpack
配置的地方可以打断点了
# 1.首次运行
# command
控制台输入命令,npm
会读取package.json
内容
# package.json
- 拿到命令
vue-cli-service serve --open
- 解析字段后
node_modules
文件夹.bin
下找到vue-cli-service
命令(安装脚手架时会自动注册此命令)
# 1.1 @vue/cli-service
- 执行此处文件
...
"bin": {
"vue-cli-service": "bin/vue-cli-service.js"
},
...
1
2
3
4
5
2
3
4
5
# vue-cli-service.js
new Service
service.run
# Service.js
- resolvePlugins
- run
- init
- server
# server.js
- registerCommand
- serve
- webpack-config
- serve
# 1.2 webpack
调用 webpack 函数,option 初始化,创建 compiler、compilation 对象、初始化 webpack 参数,得到 compiler,实例化 WebpackCLI 时,解析 node_modules/webpack/lib/webpack.js
const webpack = (options, callback) ={
let compiler
if (Array.isArray(options)) {
} else if (typeof options === "object") {
options = new WebpackOptionsDefaulter().process(options) // 用户设置的配置和默认配置合并
compiler = new Compiler(options.context) // Compiler 阶段
compiler.options = options
new NodeEnvironmentPlugin({ // 配置全局api插件
infrastructureLogging: options.infrastructureLogging,
}).apply(compiler)
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === "function") {
plugin.call(compiler, compiler)
} else {
plugin.apply(compiler) // 执行我们在配置文件中配置的所有插件
}
}
}
compiler.hooks.environment.call()
compiler.hooks.afterEnvironment.call()
//开启默认的所有插件
compiler.options = new WebpackOptionsApply().process(options, compiler)
}
return compiler
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 2.整合 options
options = new WebpackOptionsDefaulter().process(options)
1
# 3.实例化 compiler
compiler = new Compiler(options.context)
1
# 4.实例化插件,apply 插件
plugin.apply(compiler)
1
# 5.返回 compiler 对象
代码
compiler.hooks.environment.call()
compiler.hooks.afterEnvironment.call()
//开启默认的所有插件
compiler.options = new WebpackOptionsApply().process(options, compiler)
1
2
3
4
2
3
4
# 6.compiler.run()
解析 node_modules/webpack-cli/lib/bootstrap.js
const runCLI = async (args, originalModuleCompile) ={
...
await cli.run(args)
...
}
1
2
3
4
5
2
3
4
5
# 7.compile
开始编译,主要是加载入口文件,添加入口 module,run 函数运行,调用 compile 函数
class Compiler extends Tapable {
run(callback) {
...
this.hooks.beforeRun.callAsync(this, (err) ={
this.compile(onCompiled)
})
}
compile(callback) {//编译
const params = this.newCompilationParams()//创建编译器的参数
this.hooks.beforeCompile.callAsync(params, (err) ={
const compilation = this.newCompilation(params) //创建一个编译器
this.hooks.make.callAsync(compilation, (err) ={ //执行前面## EntryPlugin.js注册的make钩子函数并把编译器传递给插件
compilation.finish((err) ={
compilation.seal((err) ={
...
})
})
})
})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 8.实例化 compilation 对象
代码
newCompilation(params) {
const compilation = this.createCompilation();
compilation.fileTimestamps = this.fileTimestamps;
compilation.contextTimestamps = this.contextTimestamps;
compilation.name = this.name;
compilation.records = this.records;
compilation.compilationDependencies = params.compilationDependencies;
this.hooks.thisCompilation.call(compilation, params);
this.hooks.compilation.call(compilation, params);
return compilation;
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 9.make
调用 make 钩子,在插件中操作 compilation 的 addEntry。进行模块构建流程。
createCompilation() {
return new Compilation(this);
}
1
2
3
2
3
addEntry(context, entry, name, callback) {
this.hooks.addEntry.call(entry, name);
...
this._addModuleChain(
context,
entry,
module ={
this.entries.push(module);
},
(err, module) ={
...
this.hooks.succeedEntry.call(entry, name, module);
return callback(null, module);
}
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 10._addModuleChain
_addModuleChain
方法递归构建模块,直至所有模块构建完毕。
_addModuleChain(context, dependency, onModule, callback) {
...
const moduleFactory = this.dependencyFactories.get(Dep);
this.semaphore.acquire(() ={
moduleFactory.create(
{
contextInfo: {
issuer: "",
compiler: this.compiler.name
},
context: context,
dependencies: [dependency]
},
(err, module) ={
let afterFactory;
if (currentProfile) {
afterFactory = Date.now();
currentProfile.factory = afterFactory - start;
}
const addModuleResult = this.addModule(module);
module = addModuleResult.module;
onModule(module);
...
if (addModuleResult.build) {
this.buildModule(module, false, null, null, err ={
...
this.semaphore.release();
afterBuild();
});
} else {
this.semaphore.release();
this.waitForBuildingFinished(module, afterBuild);
}
}
);
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 11.seal
make 的回调中,调用 compilation.seal。
compilation.seal((err) ={
if (err) return callback(err)
this.hooks.afterCompile.callAsync(compilation, (err) ={
if (err) return callback(err)
return callback(null, compilation)
})
})
1
2
3
4
5
6
7
2
3
4
5
6
7
# 12.Chunk
调用 seal 方法进行 Chunk 的构建,和大包优化。
seal(callback) {
this.hooks.seal.call();
while (
this.hooks.optimizeDependenciesBasic.call(this.modules) ||
this.hooks.optimizeDependencies.call(this.modules) ||
this.hooks.optimizeDependenciesAdvanced.call(this.modules)
) {
/* empty */
}
this.hooks.afterOptimizeDependencies.call(this.modules);
this.hooks.beforeChunks.call();
for (const preparedEntrypoint of this._preparedEntrypoints) {
const module = preparedEntrypoint.module;
const name = preparedEntrypoint.name;
const chunk = this.addChunk(name);
const entrypoint = new Entrypoint(name);
entrypoint.setRuntimeChunk(chunk);
entrypoint.addOrigin(null, name, preparedEntrypoint.request);
this.namedChunkGroups.set(name, entrypoint);
this.entrypoints.set(name, entrypoint);
this.chunkGroups.push(entrypoint);
GraphHelpers.connectChunkGroupAndChunk(entrypoint, chunk);
GraphHelpers.connectChunkAndModule(chunk, module);
chunk.entryModule = module;
chunk.name = name;
this.assignDepth(module);
}
buildChunkGraph(
this,
/** @type {Entrypoint[]} */ (this.chunkGroups.slice())
);
this.sortModules(this.modules);
this.hooks.afterChunks.call(this.chunks);
this.hooks.optimize.call();
while (
this.hooks.optimizeModulesBasic.call(this.modules) ||
this.hooks.optimizeModules.call(this.modules) ||
this.hooks.optimizeModulesAdvanced.call(this.modules)
) {
/* empty */
}
this.hooks.afterOptimizeModules.call(this.modules);
while (
this.hooks.optimizeChunksBasic.call(this.chunks, this.chunkGroups) ||
this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups) ||
this.hooks.optimizeChunksAdvanced.call(this.chunks, this.chunkGroups)
) {
/* empty */
}
this.hooks.afterOptimizeChunks.call(this.chunks, this.chunkGroups);
this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err ={
if (err) {
return callback(err);
}
this.hooks.afterOptimizeTree.call(this.chunks, this.modules);
while (
this.hooks.optimizeChunkModulesBasic.call(this.chunks, this.modules) ||
this.hooks.optimizeChunkModules.call(this.chunks, this.modules) ||
this.hooks.optimizeChunkModulesAdvanced.call(this.chunks, this.modules)
) {
/* empty */
}
this.hooks.afterOptimizeChunkModules.call(this.chunks, this.modules);
const shouldRecord = this.hooks.shouldRecord.call() !== false;
this.hooks.reviveModules.call(this.modules, this.records);
this.hooks.optimizeModuleOrder.call(this.modules);
this.hooks.advancedOptimizeModuleOrder.call(this.modules);
this.hooks.beforeModuleIds.call(this.modules);
this.hooks.moduleIds.call(this.modules);
this.applyModuleIds();
this.hooks.optimizeModuleIds.call(this.modules);
this.hooks.afterOptimizeModuleIds.call(this.modules);
this.sortItemsWithModuleIds();
this.hooks.reviveChunks.call(this.chunks, this.records);
this.hooks.optimizeChunkOrder.call(this.chunks);
this.hooks.beforeChunkIds.call(this.chunks);
this.applyChunkIds();
this.hooks.optimizeChunkIds.call(this.chunks);
this.hooks.afterOptimizeChunkIds.call(this.chunks);
this.sortItemsWithChunkIds();
if (shouldRecord) {
this.hooks.recordModules.call(this.modules, this.records);
this.hooks.recordChunks.call(this.chunks, this.records);
}
this.hooks.beforeHash.call();
this.createHash();
this.hooks.afterHash.call();
if (shouldRecord) {
this.hooks.recordHash.call(this.records);
}
this.hooks.beforeModuleAssets.call();
this.createModuleAssets();
if (this.hooks.shouldGenerateChunkAssets.call() !== false) {
this.hooks.beforeChunkAssets.call();
this.createChunkAssets();
}
this.hooks.additionalChunkAssets.call(this.chunks);
this.summarizeDependencies();
if (shouldRecord) {
this.hooks.record.call(this, this.records);
}
this.hooks.additionalAssets.callAsync(err ={
if (err) {
return callback(err);
}
this.hooks.optimizeChunkAssets.callAsync(this.chunks, err ={
if (err) {
return callback(err);
}
this.hooks.afterOptimizeChunkAssets.call(this.chunks);
this.hooks.optimizeAssets.callAsync(this.assets, err ={
if (err) {
return callback(err);
}
this.hooks.afterOptimizeAssets.call(this.assets);
if (this.hooks.needAdditionalSeal.call()) {
this.unseal();
return this.seal(callback);
}
return this.hooks.afterSeal.callAsync(callback);
});
});
});
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# 13.onCompiled
构建结束进入输出阶段,调用 onCompiled 方法。调用 done 钩子后,整个构建结束。
const onCompiled = (err, compilation) ={
if (err) return finalCallback(err)
if (this.hooks.shouldEmit.call(compilation) === false) {
const stats = new Stats(compilation)
stats.startTime = startTime
stats.endTime = Date.now()
this.hooks.done.callAsync(stats, (err) ={
if (err) return finalCallback(err)
return finalCallback(null, stats)
})
return
}
//执行打包操作
this.emitAssets(compilation, (err) ={
if (err) return finalCallback(err)
if (compilation.hooks.needAdditionalPass.call()) {
compilation.needAdditionalPass = true
const stats = new Stats(compilation)
stats.startTime = startTime
stats.endTime = Date.now()
this.hooks.done.callAsync(stats, (err) ={
if (err) return finalCallback(err)
this.hooks.additionalPass.callAsync((err) ={
if (err) return finalCallback(err)
this.compile(onCompiled)
})
})
return
}
this.emitRecords((err) ={
if (err) return finalCallback(err)
const stats = new Stats(compilation)
stats.startTime = startTime
stats.endTime = Date.now()
this.hooks.done.callAsync(stats, (err) ={
if (err) return finalCallback(err)
return finalCallback(null, stats)
})
})
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42