手记

webpack 源码 - 资源加载

准备编译的代码资源

一共四个文件资源

// index.js
import { varA, varB } from './mod1'
import m2 from './mod2'

let b = 'bbb'
console.log(varA, varB, m2)

// mod1.js
export const varA = 'aaa'
export const varB = 'bbb'

// mod2.js
import { c } from './modc.js'
export default {
  m1: 'm1',
  m2: 'm2'
}

// modc.js
export const c = 'cccc'

webpack 资源加载 - 主流程

1.Compiler.js - run在 run 方法中执行 this.compile()

2.Compiler.js - compile生成 Compiltaion 实例执行 make hooks


make hooks 的实现在 SingleEntryPlugin 插件里生成 SingleEntryDependency 调用 compilation 实例的 addEntry 方法.

3.Compilation.js - addEntry - 处理入口 entry 执行 _addModuleChain

addEntry 中把 SingleEntryPlugin 生成的 dep 也就是 entry 传入执行 _addModuleChain发现执行的 _addModuleChain 回调有在 this.entries 中 push 一个 module 的线索

4.Compilation.js - _addModuleChain - factory 创建 module - addModule 到 this.modules

_addModuleChain 创建 module此时第一个 module 出现这个 module 是以为 entry 创建的

5.Compilation.js - _addModuleChain - addModule 把 module push 到 this.modules 中

5.Compilation.js - _addModuleChain - buildModule - 对 entry 的 module 进行 Loader 处理

buildModule 由 NormalModule.js 实现对远吗执行 loader 并收集依赖

此时 compilation 实例中 enties 和 modules 各自有一个 module , 这个 module 都是 entry 入口文件生成的。

此时 entry 的 module 已经分析出了 import 导入的模块语法

entry 的 module 的源码也被 babel-loader 处理成了 ES5 的代码

6.Compilation.js - _addModuleChain - afterBuild - 主入口 module 处理完毕

在 processModuleDependencies 中对 entry 的 module 的依赖进行递归调用 buildModule分析出所有的 module 放入到 this.modules 中最后进入 seal 阶段。此时 modues 已经不止 entry 的 module 了把所有 import 语句导入的 module 都收集完毕。

Compilation.js - _addModuleChain 做了什么

1.传入 entry 信息
2.moduleFactory 创建 entry 的 module, 添加到 this.modules 和 this.enties 中
3.调用 buildModule对 entry 的 module 进行 loader 调用、分析 entry 源码中的 import 导入模块语句分析出依赖信息。
4.执行 afterBuild 在 processModuleDependencies 中处理 entry module 的依赖文件处理成 module 放入 this.modules 中完成资源加载

buildModule

buildModule 中执行当前 module 类型的 module.build

进入 NormalModule.js 的 doBuild 执行 LoaderRunner.js 文件 runLoaders

这里遇到 LOADER_EXECUTION 函数里面 的 fn 就是 loader args 是 loader 处理前的源码

到这一步中间就在 loader 中执行经过 loader 出来后的代码就是 arguments 这里已经转译成 ES5 了, 执行的 callback 是 doBuild 的回调

带着 loader 处理后的源码回到 doBuild 回调中走this.parser.parse 中

带着 loader 处理后的源码进入 parse 出来 ast 对象

通过 acorn 三方模块对源码分析出 ast 描述源码的语义

parse 最后就是返回一个 state这时 state 中 module.dependencies 还是空的

源码生成的 ast 经过 walker 的处理在 state 上分析出了所有 import 文件的信息

用这种语法彻底描述出源码中的依赖关系

此时 entry 文件的就处理完了此时 compilation 实例上的 this.modules 仍然只有一个 entry module但是现在 webpack 已经通过 ast 解析出了所有 entry module 中的依赖模块的信息后面就根据这些依赖信息生成各个模块

资源递归收集依赖

对依赖进行处理发现是 import 语义的依赖才进行处理

处理收集 import 的依赖调用 addModuleDependencies方法里与 buildModule 类似工厂生成 moduleaddModule 加入到 this.modules 中进行 loader 处理源码

最后进入 afterBuild 判断当前处理的 module 结果是否继续有依赖进行递归处理

总结

1.Compiler.js - compiler.run 执行 this.compile()
2.Compiler.js - compile生成 Compiltaion 实例执行 make hooks
3.Compilation.js - addEntry - 处理入口 entry 执行 _addModuleChain
4._addModuleChain 使用工厂函数创建 module使用 module 自己的 build
5.build 在 NormalModule 中执行 doBuild 调用 LoaderRunner.js 的 runLoaders 使用 loader 处理源码
6.loader 处理完源码回到 doBuild 回调调用 Parser 的 parse
7.使用 acorn 分析源码成 ast 给 module.dependencies 添加依赖关系
8.根据依赖关系在 afterBuild 中调用 processModuleDependencies 递归处理依赖文件
9.最后把所有处理好的 module 都放入 compilation 实例的 this.modules 中进入 seal 阶段

2人推荐
随时随地看视频
慕课网APP

热门评论

我想写个插件   实现webpack的分步骤打包   就是把代码分为几个模块系统    每个系统都可以单独打包   但是每个系统之间又可以相互源码调用   我考虑用manifest 结合externals 一起实现    楼主有什么建议吗

查看全部评论