webpack打包主要流程
- webpack合并shell命令的参数和# webpack.config.js的配置得到最终Options参数
- 循环遍历plugins,加载plugin
- 初始化Compile,传入上面得到的最终Options参数
- 运行Compile.run,Compile.run中初始化Compalation,并传入callback(编译完成后出来文件写入)
- Compalation内部根据入口文件,开始使用loader编译,
- 再找出该模块依赖的模块递归遍历依赖,经过loader编译。
- 根据入口模块和依赖关系,生成包含多个模块的chunk
- 根据生成的chunk,确定输出文件,写入文件,并监听所有依赖文件的变化,当发生编译,重新编译
- webpack会在编译的过程中抛出事件,plugins执行hook钩子,hook可以调用Webpack提供的API改变Webpack的运行结果
1.webpack入口文件,如cra里的start.js
const webpack = require('webpack');
const configFactory = require('../config/webpack.config');
2. 引入webpack和webpack.config.js, 调用webpack方法得到compiler,传入options配置,合并shell语句和配置文件的参数,得到最终的配置对象。同时加载配置中的plugins
const { plugins = [] } = finalOptions;
for (const plugin of plugins) {
plugin.apply(compiler)
}
3. 用上一步得到的配置参数,初始化Compiler对象
const compiler = new Compiler(finalOptions);
4. 加载配置里面的插件, 循环调用plugin的apply方法
const { plugins } = finalOptions;
for (let plugin of plugins) {
plugin.apply(compiler);
}
class RunPlugin{
apply(compiler) {
compiler.hooks.run.tap('RunPlugin', () => {
console.log('run1:开始编译');
});
}
}
4. 执行Compiler的run方法,初始化Compilation 开始执行编译
compiler.run((err, stats) => {});
class Compiler{
constructor(options) {
this.options = options;
this.hooks = {
run: new SyncHook(),
done:new SyncHook()
}
}
run(callback) {
this.hooks.run.call();
const onCompiled = (err, stats, fileDependencies) => {
this.hooks.done.call();
}
this.compile(onCompiled);
}
compile(callback) {
let compilation = new Compilation(this.options,this);
compilation.build(callback);
}
}
5. 在Compiler里面onCompiled处理文件写入和依赖文件的监听, 最后调用完成钩子
const onCompiled = (err, stats, fileDependencies) => {}
his.compile(onCompiled);
compile(callback) {
const compilation = new Compilation(this.options);
compilation.build(callback);
}
6. Compilation中查找入口文件,loader编译入口文件,递归遍历入口文件的依赖,保证所有的依赖模块都进过loader编译。 根据入口模块和模块的依赖生成chunk。
const chunk = {
name: entryName,
entryModule,
modules: this.modules.filter(module => module.names.includes(entryName))
}
7.遍历完成后,调用onCompiled去写入文件到output到目录中,并监听依赖文件的变化,当依赖文件变化后,开始新的编译
const onCompiled = (err, stats, fileDependencies) => {
console.log('stats',stats);
console.log('fileDependencies',fileDependencies);
for (let filename in stats.assets) {
let filePath = path.join(this.options.output.path, filename);
fs.writeFileSync(filePath,stats.assets[filename],'utf8');
}
callback(err, { toJson: () => stats });
for (let fileDependency of fileDependencies) {
fs.watch(fileDependency,()=>this.compile(onCompiled));
}
this.hooks.done.call();
}
8. 在打包编译过程中,webpack会抛出一些事件,执行一些对应的钩子