webpack 4 工作原理剖析(一)

492 阅读5分钟
webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle,这是官网的介绍,我们能得到的信息就是打包、递归、构建模块,至于更多的细节则无从得知,对于我们使用来说,我们只需要掌握如何配置即可,但如果我们想自己编写插件,想学习webpack中的设计思想,那么阅读源码则是必不可少,因此我想通过阅读源码,剖析webpack工作原理,那么就让我们开始吧。(采用方式以webpack.config.js配置文件)

一、启动打包流程

1. 项目运行webpack命令

    webpack通过bin 中的配置运行bin/webpack.js文件,代码中会判断是否安装webpack-cli和webpack-command。如果两个都没有安装会提示安装webpack-cli,如果两个都安装了会抛出错误,我本地是安装了webpack-cli,所以程序会先引入webpack-cli下package.json文件,然后再引入bin中webpack-cli对应的bin/cli.js。(webpack 3中,webpack本身和它的CLI以前都是在同一个包中,但在webpack 4中,已经将两者分开了。webpack专注打包,与命令行交互的逻辑则放在webpack-cli中)

2. option配置项初始化

    在cli.js中,会使用yargs.parse这个方法去解析process.argv,拿到命令行中输入的参数-p  -d 这种,将拿到的参数传入到工具类中,这个工具类是专门用来获取options的,它会根据我们输入的参数,生成配置项对象,在默认不配置--config的情况下,工具类会去读取项目中根目录下webpack.config.js中的配置,然后和通过命令行参数生成的配置项对象合并,最后然后返回配置信息。


3. 创建Compiler对象

    在拿到配置之后,cli.js会引入webpack这个包,执行webpack这个方法(参数为配置项options),然后:

  1. 校验options配置项,如果有错误则抛出
  2. 判断options是否是个数组,如果是数组则执行多路打包,否则执行单路打包(我这里是单路打包)
  3. 完善options配置:webpack中内置了一份完整默认配置,比如在没有配置文件的情况下,默认会把src/index当成入口,原因就在于默认配置中配置了entry为src/index,在我们有配置文件的情况下,webpack会将配置文件中的配置信息和内置配置合并,相同的项会以配置文件优先,这样就可以得到一份完整的配置。
  4. 创建Compiler对象
  5. 创建内置环境插件,并触发。


    Compiler是webpack中的核心,Compiler继承自Tapable,Tapable是一套事件流机制,内置了很多钩子类,事件流机制的 “钩子” 大致可以分为两个类别,“同步” 和 “异步”,“异步” 又分为两个类别,“并行” 和 “串行”,而 “同步” 的钩子都是串行的。以同步钩子类来举例,我们创建出钩子类的实例,然后可以在实例上注册方法a,b,c 在合适的时机触发方法,方法会按照注册的顺序依次执行。

    Compiler整个过程中只会实例一次,Compiler中有一个hooks属性,这个属性是一个对象,这个对象的属性里面包含了大量的同步和异步钩子(钩子类new 出来的实例),钩子实例用来注册和调用插件,简单点说,就是比如我把整个构建过程分成几个周期,准备、构建前,构建中,构建完成,在这个阶段我都实例好对应的钩子实例,用来注册事件函数,然后在构建周期去执行相应钩子实例上注册的函数,也就是说webpack开放钩子实例提供注册事件的能力,在准备中你要做什么,在构建前你要做什么,这些我都开放给使用者自己去注册事件去完成,这样就良好的实现了扩展性。


4. 启动构建

    Compiler对象已经创建完成,程序会判断是否是watch模式,如果是watch模式,那么就是以watch模式启动,否则直接用run启动。run方法中会先判断是否在运行中,运行中就直接抛错,否则就先调用beforeRun,run这两个钩子上注册的事件,然后再执行readRecords,读取是否有记录信息,没有就调用compile方法进入编译。



二、开始编译

   compile方法会先调用beforeCompile钩子,执行完注册事件后再调用compile钩子,再实例一个Compilation对象,Compilation是用来负责创建bundles的,包含了一次构建中全部的资源和信息,在开发环境时,每次更改文件,都会重新创建Compilation,打包资源,最后将打包之后的内容存入到内存中。


    Compilation的实例创建完成之后会,会调用一个make的钩子,进入到make的阶段,这个阶段才是真正的重头戏。。。