开始阶段
1、从命令行输出命令之后,先去node_modules下的.bin找到webpack相关的执行文件.cmd(查看源码我们可以直到在webpack中的packags.json中定义了bin字段,用于指定脚本执行的文件)这一步将得到命令行输入的参数对象
2、根据上一步得到的参数,构建全局唯一的compiler对象,实例化此对象
3、为compiler对象加载插件配置(插件在此执行,xxx-plugin.apply(compiler),compiler实例化对象是参数),加载完成之后,启动编译compiler.run()----自动编译
编译阶段
1、从Entry入口开始,寻找Entry中的入口文件
2、根据入口文件,去加载Loader(Loader在此执行),将得到的结果转化成AST语法树,根据得到的AST去继续寻找要依赖的模块,重复这一步,直到所有的依赖都被Loader加载且转换成了AST。
3、得到所有的模块和它们之间的依赖关系
输出阶段
1、根据上一步得到的结果(模块和依赖关系),将它们组成一个Chunk,使用webpack_require代替import和require操作,每一个Chunk都是一个单独输出的文件,将此文件添加到输出列表中(这是最后修改文件状态)
2、根据上一步的结果,根据输出配置,将文件内容写进文件系统中
值得注意的点
在webpack一次完整的工作流程中,会有两个重要的对象
compiler对象
- compiler是webpack完整运行一次的对象,在开始阶段就会被实例化
- 每一个webpack执行都只会有一个compiler对象,可以看作是webpack实例
- compiler由webpack的配置,options、Loaders、plugins等等
compilation对象
- compilation是包含了当前的模块资源,信息等的对象
- 当webpack以开发模式运行时,当检测到一个文件内容发生变化,那么就会生成一个新的compilation对象
- compilation对象也可以读取到compiler对象
二者区别
complier对象代表了一个完整的webpack生命周期,从开始到关闭。
compilation代表的是webpack执行了一次编译
webpack生成的文件
webpack会将所有模块打包成一个执行文件bundle.js,这个文件内部其实就是一个大型的IIFE函数,模块就变成这个IIFE函数的一个个参数对象传入,这个IIFE函数__webpack_require__模拟了require函数,通过传入的moduleId辨别,执行的模块,简写如下:
(funct工on(modules) {//模拟 require 语句
function __webpack_require__(moduleId) {
//执行存放所有模块数组中的第 0 个模块__webpack_require__(0);
}) ( [/*存放所有模块的数组*)
这个__webpack_require__还做了缓存优化,第一次执行模块时,将结果缓存,第二次执行该模块(此模块未被改变)时,直接读取上一次执行的结果