webpack 可以分为 4 个阶段:
- 初始化阶段 - webpack
- 合并配置项
- 创建Compiler
- 注册插件
- 编译阶段 - build
- 读取入口文件
- 从入口文件开始编译
- 调用 loader 对源代码进行转换
- 借助 babel 解析为 AST 收集依赖模块
- 递归对依赖模块进行编译操作
- 生成阶段 - seal
- 创建 chunk 对象
- 生成 assets 对象
- 输出阶段 - emit
初始化阶段
读取和合并配置信息
首先会执行 webpack 函数读取和合并配置信息,配置信息来源主要有两种方式:
- 第一种是通过 webpack.config.js 做配置,该文件中主要包括:入口文件、输出位置、loader 和 plugin
- 第二种是通过命令行的形式做配置,比如 --mode=production。命令行的权要高于配置文件。
创建 complier 对象
然后通过 Compiler 构造函数,传入合并的配置项,获得 compiler 实例。
Compiler 构造函数中有 run 方法和 emitAssets 方法。当需要执行编译时就会调用 run 方法。
注册插件
接着注册插件。插件的目的是在合适的时机干预构建过程。
插件可以有两种形式:
- 可以是函数
- 也可以是对象,但是对象需要提供一个 apply 方法,并接收 compiler 实例作为参数
注册插件的过程就是遍历配置文件中的 plugins 数组,并依次执行该插件。当插件为函数时:plugin.call(compiler, compiler)。如果插件是一个对象,需要提供 apply 方法:plugin.apply(compiler)。
初始化的阶段完成。
编译阶段
编译工作的起点是调用 compiler.run 方法。run 的主要工作:
- 发起构建通知,触发 hooks.run 通知相关插件;
- 创建 compilation 编译对象;
- 读取 entry 入口文件;
- 编译 entry 入口文件;
创建 compilation 对象
执行 const compilation = new Compilation(this);创建 compilation 对象。
模块的 build (代码构建)和 seal (代码生成)都是 compilation 对象实现的。
然后执行 compilation.buildI();开始编译模块。
读取 entry 入口文件
构建模块首先从 entry 入口模块开始,此时首要工作是根据配置文件拿到入口模块信息。
编译 entry 入口文件
拿到入口文件后,依次对每个入口进行构建。
构建阶段执行如下操作:
- 通过 fs 模块读取入口文件的内容;
- 调用 loader 来转换文件内容;
- 为模块创建 module 对象,通过 AST解析源代码收集依赖,并改写依赖模块的路径;
- 如果存在依赖模块,递归进行上述三步操作;
loader 本身是一个 JS 函数,接收模块文件的源代码作为参数,经过加工改造后返回新的代码。
执行 webpack 模块编译逻辑:首先读取文件原始代码,然后调用 loader 进行处理,最后调用 webpack 进行模块编译 为模块创建 module 对象。
- 创建 module 对象;
- 对 module code 解析为 AST 语法树;
- 遍历 AST 去识别 require 模块语法,将模块收集在 module.dependencies 之中,并改写 require 语法为 webpack_require;
- 将修改后的 AST 转换为源代码;
- 若存在依赖模块,深度递归构建依赖模块。
生成阶段
在「编译阶段」会将一个个文件构建成 module 存储在 this.modules 之中。
在「生成阶段」,会根据 entry 创建对应 chunk 并从 this.modules 中查找被 entry 所依赖的 module 集合。
根据 entry 创建 chunk。根据入口文件和依赖模块组装chunks:
首先遍历 this.entries 集合,createChunk 依次执行 createChunk 方法。在 createChunk 方法中创建 chunk 并将 chunk 添加到 this.chunks 集合中。
最后,结合 runtime webpack 模块机制运行代码,经过拼接生成最终的 assets 产物。
根据 chunk 创建 assets。为每一个 chunk 文件代码拼接 runtime 运行时语法。this.assets 的数据结构是一个对象,chunk文件名为 key 和 文件内容为 value。
输出阶段
调用 this.emitAssets 方法开始输出阶段。
首先会调用 Plugin emit 钩子:this.hooks.emit.call()。然后创建输入目录。再将 assets 中的内容写入文件系统中:遍历 this.assets 对象,调用fs.writeFileSync(filePath, assets[filePath])。结束之后触发钩子:this.hooks.done.call()。
结束。
标签:文件,插件,流程,入口,编译,webpack,构建,模块 From: https://www.cnblogs.com/rocenjs/p/18366174