webpack 在不同阶段做的事情

1. webpack 初始化过程
从 webpack 项目 webpack.js 文件 webpack 方法出发,可以看到初始化过程如下:

  1. 将命令行参数和用户的配置文件进行合并。
  2. 调用 getValidateSchema 对配置进行校验。
  3. 调用 createCompiler 创建 Compiler 对象。
    i. 将用户配置和默认配置进行合并处理。
    ii. 实例化 Compiler。
    iii. 实例化 NodeEnvironmentPlugin。
    iv. 处理用户配置的 plugins,执行 plugin 的 apply 方法。
    v. 触发 environment 和 afterEnvironment 上注册的事件。
    vi. 注册 webpack 内部插件。
    vii. 触发 initialize 事件。
// lib/webpack.js 122 行 部分代码省略处理
const create = () => {
if (!webpackOptionsSchemaCheck(options)) {
// 校验参数
getValidateSchema()(webpackOptionsSchema, options);
}
// 创建 compiler 对象
compiler = createCompiler(webpackOptions);
};

// lib/webpack.js 57 行
const createCompiler = (rawOptions) => {
// 统一合并处理参数
const options = getNormalizedWebpackOptions(rawOptions);
applyWebpackOptionsBaseDefaults(options);
// 实例化 compiler
const compiler = new Compiler(options.context);
// 把 options 挂载到对象上
compiler.options = options;
// NodeEnvironmentPlugin 是对 fs 模块的封装,用来处理文件输入输出等
new NodeEnvironmentPlugin({
infrastructureLogging: options.infrastructureLogging,
}).apply(compiler);
// 注册用户配置插件
if (Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
if (typeof plugin === "function") {
plugin.call(compiler, compiler);
} else {
plugin.apply(compiler);
}
}
}
applyWebpackOptionsDefaults(options);
// 触发 environment 和 afterEnvironment 上注册的事件

2. webpack 构建阶段做了什么
在 webpack 函数执行完之后,就到主要的构建阶段,首先执行 compiler.run(),然后触发一 系列钩子函数,执行 compiler.compile()。

  1. 在实例化 compiler 之后,执行 compiler.run()。
  2. 执行 newCompilation 函数,调用 createCompilation 初始化 Compilation 对象。
  3. 执行 _addEntryItem 将入口文件存入 this.entries(map 对象),遍历 this.entries 对 象构建 chunk。
  4. 执行 handleModuleCreation,开始创建模块实例。
  5. 执行 moduleFactory.create 创建模块。
    i. 执行 factory.hooks.factorize.call 钩子,然后会调用 ExternalModuleFactoryPlugin 中注册的钩子,用于配置外部文件的模块加载方
    ii. 使用 enhanced-resolve 解析模块和 loader 的真实绝对路径。
    iii. 执行 new NormalModule()创建 module 实例。
  6. 执行 addModule,存储 module。
  7. 执行 buildModule,添加模块到模块队列 buildQueue,开始构建模块, 这里会调用 normalModule 中的 build 开启构建。
    i. 创建 loader 上下文。
    ii. 执行 runLoaders,通过 enhanced-resolve 解析得到的模块和 loader 的路径获取 函数,执行 loader。
    iii. 生成模块的 hash。
  8. 所有依赖都解析完毕后,构建阶段结束。
// 构建过程涉及流程比较复杂,代码会做省略

// lib/webpack.js 1284行
// 开启编译流程
compiler.run((err, stats) => {
compiler.close(err2 => {
callback(err | | err2, stats);
});
});

// lib/compiler.js 
// 开启编译流程
compile(callback) {
const params = this.newCompilationParams();
// 创建 Compilation 对象
const Compilation = this.newCompilation(params);
}

// lib/Compilation.js 
// 确认入口文件
addEntry() {
this._addEntryItem();
}

// lib/Compilation.js 
// 开始创建模块流程,创建模块实例
addModuleTree() {
this.handleModuleCreation()
}

// lib/Compilation.js 
                                                                                                                  
handleModuleCreation() {
this.factorizeModule()
}

// lib/Compilation.js 

// 添加到创建模块队列,执行创建模块
factorizeModule(options, callback) this.factorizeQueue.add(options,
}

// lib/Compilation.js // 保存需要构建模块
_addModule(module, callback) {
this.modules.add(module); }

// lib/Compilation.js       // 添加模块进模块编译队列,开始编译



{
callback);

buildModule(module, callback) this.buildQueue.add(module,
}


{
callback);

3. webpack 生成阶段做了什么
构建阶段围绕 module 展开,生成阶段则围绕 chunks 展开。经过构建阶段之后,webpack 得到足够的模块内容与模块关系信息,之后通过 Compilation.seal 函数生成最终资源。
3.1 生成产物

执行 Compilation.seal 进行产物的封装。

  1. 构建本次编译的 ChunkGraph 对象,执行 buildChunkGraph,这里会将 import()、 require.ensure 等方法生成的动态模块添加到 chunks 中。
  2. 遍历 Compilation.modules 集合,将 module 按 entry/动态引入 的规则分配给不同 的 Chunk 对象。
  3. 调用 Compilation.emitAssets 方法将 assets 信息记录到 Compilation.assets 对象 中。
  4. 执行 hooks.optimizeChunkModules 的钩子,这里开始进行代码生成和封装。
    i. 执行一系列钩子函数(reviveModules, moduleId, optimizeChunkIds 等)。
    ii. 执行 createModuleHashes 更新模块 hash。
    iii. 执行 JavascriptGenerator 生成模块代码,这里会遍历 modules,创建构建任务, 循环使用 JavascriptGenerator 构建代码,这时会将 import 等模块引入方式替换 为 webpack_require 等,并将生成结果存入缓存。
    iv. 执行 processRuntimeRequirements,根据生成的内容所使用到的 webpack_require 的函数,添加对应的代码。
    v. 执行 createHash 创建 chunk 的 hash。
    vi. 执行 clearAssets 清除 chunk 的 files 和 auxiliary,这里缓存的是生成的 chunk 的 文件名,主要是清除上次构建产生的废弃内容。

3.2 文件输出

回到 Compiler 的流程中,执行 onCompiled 回调。

  1. 触发 shouldEmit 钩子函数,这里是最后能优化产物的钩子。
  2. 遍历 module 集合,根据 entry 配置及引入资源的方式,将 module 分配到不同的 chunk。
  3. 遍历 chunk 集合,调用 Compilation.emitAsset 方法标记 chunk 的输出规则,即转化
    为 assets 集合。
  4. 写入本地文件,用的是 webpack 函数执行时初始化的文件流工具。
  5. 执行 done 钩子函数,这里会执行 compiler.run() 的回调,再执行 compiler.close(), 然后执行持久化存储(前提是使用的 filesystem 缓存模式)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值