通过Webpack 官网banner图很直观的读懂它的功能。就是将应用的各种互相依赖的资源文件分类归纳聚集打包输出。整个打包过程主要做了2件事:
当 webpack 处理应用程序时,它会根据命令行参数中或配置文件中定义的模块列表开始处理, 从 入口 开始,webpack 会递归的构建一个 依赖关系图,这个依赖图包含着应用程序中所需的每个模块,然后将所有模块打包为少量的 bundle。
有了这个依赖关系树过后, Webpack 会遍历(递归)这个依赖树,找到每个节点对应的资源文件,然后根据配置选项中的 Loader 配置,交给对应的 Loader 去加载这个模块,最后将加载的结果放入 bundle.js(打包结果)中,从而实现整个项目的打包。
Webpack是一个开箱即用的工具,若在日常开发使用一些前端框架的脚手架比如vue-cli,那基本上不需要接触到webpack细节。但开发中往往还是会遇到各种各样的问题,如果不了解webpack的一些基础原理,届时就会手忙脚乱了,方寸大乱了。
在深入了解webpack原理之前最好还是带着问题去查阅源码,否则进入到源码的世界就会像无头苍蝇一样到处乱撞,耗时费力。
因为webpack的核心机制是loader和plugin,所以大多数问题会围绕它们展开如下:
node --inspect-brk ./node_modules/webpack/bin/webpack.js
其中参数--inspect-brk就是以调试模式启动node:
会观察到输出:
在弹出窗口点击超链接"Open Dedicated DevTools for Node.
此时在第一步的命令行窗口里,出现一行新的提示信息:debugger attached。
此时Chrome窗口弹出来了,断点停留在webpack.js第一行处,这时候就可以开始调试webpack了。
紧接着代码会进入到webpack-cli文件,Webpack CLI 的作用就是将 CLI 参数和 Webpack 配置文件中的配置整合,得到一个完整的配置对象。
有了配置选项过后,开始载入 Webpack 核心模块,传入配置选项,创建 Compiler 对象,这个 Compiler 对象就是整个 Webpack 工作过程中最核心的对象了,负责完成整个项目的构建工作。
观察代码,发现 options 不仅仅可以是一个对象,还可以是一个数组。如果options是一个数组,那么 Webpack 内部创建的就是一个 MultiCompiler,也就是说 Webpack 应该支持同时开启多路打包,配置数组中的每一个成员就是一个独立的配置选项。而如果我们传入的是普通的对象,就会按照我们最熟悉的方式创建一个 Compiler 对象,进行单线打包。
这里是单线打包,核心代码是compiler = createCompiler(webpackOptions);此处代码大概分3个阶段。
完成 Compiler 对象的创建过后,紧接着这里的代码开始判断配置选项中是否启用了监视模式,我们这里没有开启,就直接运行run方法开始构建应用。
run方法内部先触发了beforeRun 和 run 两个钩子,然后调用了当前对象的 compile 方法,开始真正开始编译整个项目。这个(代码在compiler.js)
compile 方法内部主要就是创建了一个 Compilation 对象,可以理解为一次构建过程中的上下文对象,里面包含了这次构建中全部的资源和信息。
创建完 Compilation 对象过后,紧接着触发了一个叫作 make 的钩子,整个构建过程最核心的 make 阶段。
由于webpack采用事件触发机制,所以这里代码调试会比较困难。调试最好的方式是找到在哪里注册了make事件。Webpack 的插件系统是基于官方自己的 Tapable 库实现的,我们想要知道在哪里注册了某个事件,需要知道如何注册的事件。
Tapable注册事件方式:
所以开发工具搜索源代码中的 hooks.make,就应该能够找到事件注册的位置
make 阶段主体的目标就是:根据 entry 配置找到入口模块,开始依次递归出所有依赖,形成依赖关系树,然后将递归到的每个模块交给不同的 Loader 处理。这个插件中调用了 Compilation 对象的 addEntry 方法,开始解析我们源代码中的入口,可以从这里开始进入调试。
后续的流程大概如下: