素材巴巴 > 程序开发 >

webpack5 配置14 打包分析 webpack源码

程序开发 2023-09-05 09:14:12

打包时间的分析

我们执行build后,显示了一个总的时间,在这里插入图片描述
根据我们电脑配置不同,node版本不同,webpack不同,打包的时间肯定是有出入的。
但是我们想知道每个阶段,每个插件或者每个Loader所消耗的时间。
这里可以借助一个插件,speed-measure-webpack-plugin,但这个插件与部分插件存在兼容性问题,所以使用的时候要注意让不兼容的插件不显示他的打包时间就可以了。
cnpm install speed-measure-webpack-plugin -D
这个插件的使用有点特殊,他是将我们整个配置对象包裹进来,然后根据我们配置的plugin,loader,计算出打包时间。
在这里插入图片描述
在这里插入图片描述
执行后
在这里插入图片描述
可以看到每个插件的打包时间都计算出来了,但是如果里面的loader或者插件与该插件不兼容的话就会报错。
在这里插入图片描述
像这里就报错了
所以我们的做法可以先注射掉报错的插件,因为我们此次打包的目的是计算时间,然后真正打包再还原。

分析打包后的文件大小等等

在这里插入图片描述
在这里插入图片描述
再把这个文件放到
http://webpack.github.io/analyse/
在这里插入图片描述
就可以看到很多具体的细节。

在社区也有很多插件可以分析。

在这里插入图片描述
用的比较多的是wbepack-bundle-analyzer
cnpm install webpack-bundle-analyzer -D
在这里插入图片描述

在这里插入图片描述
这个包的使用也非常简单。他会在我们打包后直接开启一个本地服务,在这里插入图片描述
在这里插入图片描述

在这个本地服务就能看到哪些包比较大,也可以看到哪些包是从哪些模块打包出来的。

webpack源码

webpack启动流程

当我们执行npm run build后,在这里插入图片描述
本质上会找到node_modules下的bin的webpack模块里面的在这里插入图片描述
在这里插入图片描述
本质上执行的是这里面的代码。
读一下这里面的源码
首先可以看到三个函数,runComand, isInstalled, runCil,先不看
在这里插入图片描述
可以看到定义一个cli的对象,里面的installed判断当前模块是否安装了webpack-cli
接着继续走
在这里插入图片描述
判断房钱是否安装了,没安装的话,就会提示你比如安装wbepack-cli。
在这里插入图片描述
接着在这里插入图片描述
realine可以接受你在命令行的输入。如果你输入了yes在这里插入图片描述
他就会执行runcOMMAND(‘webpack-cli’)去安装这个包,所以runCommand这个函数就是用来安装包的。
安装成功后,或者你本来就有安装
在这里插入图片描述
就会执行runCli函数,并将cli对象传给他。在这里插入图片描述
这里的cli.package是webpack-cli,也就是他从webpack-cli目录下的package.json文件,让然后requrie进来,赋予pkg,这时候pkg就是webpack-cli下的package.json文件的对象,然后通过这个对象下的bin的webpack属性拿到文件
在这里插入图片描述
也就是做了这么多操作就是为了拿到这个cli.js文件,在这里插入图片描述
然后require,就是运行的意思,就是说第二步就走到了webpack-cli的cli.js文件。
在这里插入图片描述
这个文件做的其实也很简单,utils是它封装的工具,用来判断webpack安装了吗,如果安装了执行runCLI函数,如果没有。
在这里插入图片描述
会提示你安装然后成功的话再执行runCLI函数。所以说到底就是要执行runCLI函数
让我们看看runCLI是啥
在这里插入图片描述
可以看到其实就两部,第一步,new一个webpackCLI实例,然后赋值,然后执行实例方法run。那我们先看看webpackcli这个类在new的时候做了啥。在这里插入图片描述
webpack-cli构造函数主要做的事情就是构造一个webpack属性,这个属性就require(webpack)的结果,是一个函数。
这个函数最终要做的事情,在这里插入图片描述
这里传入两个参数,一个是config.options,就是我们的配置信息,这个配置信息不止webpack.config.js文件,还有package.json后面跟着的命令行在这里插入图片描述
像这些,webpack会整合然后换入webpack函数q去调用。
在这里插入图片描述
第二个参数就是callback回调函数,当我们传入的时候会自动调用complier的run方法,而没传的时候手动拿到complier.run(然后传入(error, stats)=》{})

然后我们继续看第二步在这里插入图片描述

执行webpack实例的run方法,
在这里插入图片描述

这里一开始创建了很多对象,这个run方法最重要的就是两个
1在这里插入图片描述
这个makeComman方法,this是指向wbepakc实例的,所以在这里插入图片描述
在webpackCLI这个类中也可以看到这个方法的存在,这个方法做了很多命令行的配置,最终的目的也是为了执行在这里插入图片描述
this,makeOption(这个方法是真正的生成我们配置文件的代码)
在这里插入图片描述
走到这步配置文件就基本生成代码了。
然后回到makeCommand,在这里插入图片描述
可以看到第三个参数是有执行BuildCommand这个函数,在这里插入图片描述
这个BuildCommand的主要作用就是调用最后面的,在这里插入图片描述
createComplier,这个函数就是我们一开始讲到的this.webpack做的事情,在这里插入图片描述
此时配置代码已经生成好了,就在config.options里面,在这里插入图片描述
而callback就是buildComman里面定义的一个函数。
this.webpack最后就是最终生成了一个complier。

所以目前为止,webpack-cli做的事情就很简单了,首先是直接判断有没有安装wbepack,然后new 一个webpackcli实例,然后引入this.webpack属性,然后执行一大串的代码,如一开始的this.makeCommand,这个里面就会调用this.makeOptions,这个函数的作用就是将webpack.config.js文件跟package.json后面的参数合并起来。接着又调用了this.buildComman方法,里面定义了一个callback,并将其作为参数调用this.createComplier,这个函数就是整个wbepack-cli做的关键的事情,在里面调用了this.webpack(this.makeOptions整合而成的配置对象config.options, callback)生成一个complier。所以webpack-cli可以说就是帮助我们将package.json与webpack.config.js配置文件的信息整合在一起。更多细节可以自己去看源码。
我们可以验证下,wbepack-cli做的事情,因为react vue的话也是没有用到wbepack-cli这个工具的。

我们自己定义一个build.js文件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
打包成功,进一步验证了webpack-cli的主要作用就是将package.json文件执行的命令行后面的参数与webpack配置文件整合而成。

小结: 执行run build后,执行webpack,找到node_modules下的bin下的webpack文件,然后判断有没有安装webpack-cli,接着就是执行runCli,执行这个会找到webpack-cli下的cli文件,判断有没有安装webpack,然后执行runCLI函数,这个函数会直接new一个webpackCLI实例,这个实例有很多属性和方法,而且内部this.webpack = require(‘webpack’),然后执行webpackCLI实力的run方法,这个方法里面又调用了this.makeCommand,this.makeCommand主要做了两个事情,一是调用this.makeOptions去生产整合的配置代码,一个是执行this.buildCommand去执行this.createComplier函数,这个函数就是执行this.webpack(config.options,callback)去生成最终的解析器,然后执行complier.run去打包我们的文件。所以webpack-cli的任务就是中间这一层,帮助我们整合package.sjon文件的配置与webpack的配置文件整合起来。

看一张图(来自coderwhy老师)
在这里插入图片描述

webpack源码阅读

我们现在研究下在这里插入图片描述
webpack拿到我们的配置对象做了什么事情
先从github上下载源码
在这里插入图片描述
在这里插入图片描述
然后现在我们要通过他这份源代码进行编译。
导入的webpack是源代码里面的webpack。
现在看看我们的webpack源码
在这里插入图片描述
webpack是一个函数,接受两个参数,一个是配置对象,一个是回调函数。一开始创建了一个Create哈函数目前我们不需要用到,然后判断有没有传callback
在这里插入图片描述
有传的话,就会判断有没有watch,然后执行run函数。这个run里面还会执行callback函数。然后在返回complier。而没有的话他会执行created函数。
在这里插入图片描述

在这里插入图片描述
可以看到create函数定义了一个complier变量然后返回了。
在这里插入图片描述
这个create会判断当前传入的是数组还是对象然后调用不同函数生成complier
在这里插入图片描述
在这里插入图片描述
这两个函数就是用来创建complier的。
主要是第二个,createComplier,是webpack比较核心的东西。
通过Complier类创建出一个对象。这个对象肯定还有一个run方法。
然后第二步就是注册所有的插件,通过options.plugins拿到所有的插件配置,注册的时候会做一个判断,因为我们一般使用new 插件(),这个是一个对象,他会判断是函数还是对象,函数的话会通过在这里插入图片描述
call让complier去执行一次,如果是对象,就通过apply,这个apply不是函数那个apply绑定this那个,而是插件本身自己定义的方法。这个的作用是通过apply,传入complier对象,然后注册。这时候只是注册,并不是执行。插件的执行其实是贯穿整个webpack的生周,就是整个生命周期都可能有plugin的运行。
第三步 注册完后就调用两个钩子
第四步 在这里插入图片描述
处理器它的属性, 如output等等,主要是调用了process方法在这里插入图片描述
这个方法的主要作用是将传入的属性转成Plugin,注入到webpack的声明周期中。
在这里插入图片描述
每个插件都有apply方法,用来注入到complier里面去。
在这里插入图片描述
这里还有devtool属性,也是转成了一个插件。入口的话本质上也是转成插件。
注入插件的目的是将complier对象传入apply函数,然后通过complier对象监听做一些其他的事情。比如hooks的监听。
接着回到createComplier,里面创建complier实例就是通过Complier这个类的在这里插入图片描述
创建了一大堆的hooks在这里插入图片描述
因为我们注册的时候是将complier传入到apply的,然后在apply内部就可以通过complier.hooks.xxx去调用到new的时候创建的hooks对象里面的一些属性。什么时候调用呢?在webpack的某个生命周期,可以通过complier.hooks.xx.call()去调用。一调用的话因为我们之前apply的时候已经注册过了,所以会直接执行。所以plugin的执行阶段,是在wbepack的任意时间执行的,取决于你监听的是哪个hooks。

现在我们回到最初,拿到Complier的时候调用的run方法在这里插入图片描述

先来看大体结构,主要是定义了三个方法,分别是finalCallback,onCompiled,还有run方法。 最后面做判断的时候,都是会执行run方法,所以我们首先看下run方法
在这里插入图片描述
它是先调用某个hooks。因为这个hooks在之前构造函数的时候就被注册了,所以会被执行。里面的finalCallback可以推断是用来处理错误的函数,在这里插入图片描述
然后没有错误的话,就是调用this.complie在这里插入图片描述
传入一个回调函数,这也是最重要的代码。
当编译成功后
在这里插入图片描述
可以看到这里是计算总时间等一系列操作的代码。所以我们重点关注下complie函数。
在这里插入图片描述
complier与compilation的区别
在这里插入图片描述
complier是webpack构建之初就会创建的对象,并在整个webpack的生周后期都存在。但是compliation是到了准备编译模块才会创建,主要存在于complie之后,make之前。
两者的区别
在这里插入图片描述
complier是整个生命周期都可以存在用的,初始化complier是要做很多事情的,所以当你源代码改变时,complier是可以被服用的,而compliation的话是根据源码内容的,所以源代码改变就会改变。那什么时候complier会改变呢?当我们修改了webpack的配置,并且重新执行run build。才会重新创建。
回到主题,complier函数做了啥,一开始初始化了compilation的参数,赋给params,然后又继续调用Hooks了。在这里插入图片描述
调用hooks之后,我们的compilation才被正常创建出来。通过this.newCompilation(params)
在这里插入图片描述
所以我们刚才说的compilation对象主要创建于complier之后,make之前,因为它是在这两个钩子中间被创建的。
make阶段后又执行finishMake,接着又是afterCompile.
在这里插入图片描述
但是编译的具体模块是在make的hooks运行的,因为我们之前已经注册过这个hooks了
在这里插入图片描述
this指向的是我们的complier,所以this.hoos.,make就会执行到当前的代码。
传入一个compilations对象,调用了compliation.addEntry的方法,这个方法是用来创建入口的
在这里插入图片描述
这个方法主要做的事情是创建模块图,addModule,buildModule,然后将这些模块放到this.buildQueue去。然后调用this._buildModule方法,拿到我们的每个模块Module,在通过Module.neeBUild去判断需不需要构建,然后在调用module.build开始构建模块。这个build调用的时候就会执行runLoader,因为我们构建模块的时候是需要借助loader的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

小结 webpack的执行,

webpack函数其实很简单,它会根据你有没有传callback,去判断执不执行run函数。但是他始终会做的事情就是通过create函数去创建complier对象,然后返回,如果传入了callback就会顺便帮助你执行run函数。create这个函数会根据你传入的配置是对象还是数组,而调用不同的函数,比如数组是createMultCompier,而对象则是我们普遍使用的,调用createComplier,顾名思义就是创建complier对象,这个函数主要做了四件事情,通过complier类创建complier对象,注册所有的插件,(通过apply()),调用钩子,处理除了plugin其他的属性。然后complier类的构造函数是会注册很多的钩子的。
接着我们看看根据有没有传入callback去执不执行run函数,这个run函数主要是实现了三个函数,一个run,一个finalCallback(失败的回调),一个onComplied(成功的回调)。最重要是是这个run函数,里面会执行this.complier()去准备编译我们的模块。
complier这块的话就会创建了compilation对象,这个跟complier对象是有一定去别的。而他主要也是在钩子函数complier之后,make之前调用的。最重要的是这个make钩子,他是在之前就注册过了的。他可以配置入口参数entry,并且创建模块图,然后把每个模块放到htis.buildQueue去,通过一个方法去执行每个模块的build方法,也就是去构建每个模块,而在这里面又会执行runLoader,因为构建模块的时候是需要Loader来处理的,所以loader的主要发挥时间也是在这。

标签:

素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。