Analyse du code source de base de webpack5

répertoire de la série webpack

  1. La configuration de base de webpack5
  2. Le principe modulaire de webpack5
  3. Compatibilité Babel/ESlint/navigateur de webpack5
  4. Optimisation des performances de webpack5
  5. Implémentation de Loader et Plugin de webpack5
  6. Analyse du code source de base de webpack5

Le principe de lancement de la commande webpack

Nous commençons généralement webpack en configurant les commandes webpack dans les scripts npm, telles que les commandes de build packaging

"scripts" {
  "build": "webpack --config ./config/webpack.common.js --env production"
}
复制代码

1. webpack/bin/webpack.js

Nous utilisons la commande webpack pour commencer à l'aide d' webpack-clioutils.Lorsque nous installons webpack, il y a une ligne d' binattributs dans le package.json de webpack, puis npm utilisera le nom de binl'attribut à l'intérieur webpackcomme nom de fichier, et le nouveau contenu dans ce fichier dans le binrépertoire installez ci-dessouswebpack.jsnode_modules/.bin

Snipaste_2022-03-28_20-52-45.png

Snipaste_2022-03-28_20-59-24.png

Ainsi, lorsque nous exécutons la commande webpack, nous exécutons en fait webpack/bin/webpack.jsce fichier.

Jetons un œil à webpack/bin/webpack.jsce fichier, le processus principal est le suivant

  1. Définir d'abord un cliobjet
  2. Pour juger si l' attribut cliinterne est vraiinstalled
  3. mettre en œuvrerunCli

Snipaste_2022-03-28_21-03-28.png

C'est-à-dire qu'il va d'abord déterminer si webpack-clile package est installé. Si ce n'est pas le cas, il entrera dans l'installation conditionnelle, sinon il sera exécuté runCli. En général, on utilise également la commande package pour installer webpacket installer webpack-cliles deux packages à en même temps, donc l'essentiel est de voirrunCli

1. Exécutez runCli

Snipaste_2022-03-28_21-14-38.png

L'essentiel dans runcli est de fusionner les valeurs d'attribut dans la cli en un chemin, pkgPathc'est-à- webpack-cli/package.jsondire pkgl' webpack-cli/package.jsonobjet, et webpack-cli/package.jsonla valeur d'attribut bincorrespondante webpack-clide l'attribut dans la cli estbin/cli.js

Snipaste_2022-03-28_21-20-56.png

所以最后拼接的是webpack-cli/bin/cli.js这个路径,将调用这个文件

2. webpack-cli/bin/cli.js

1. 执行runCLI

这个文件其实主要执行的是runCLI,并且将命令后的参数一起传进去

Snipaste_2022-03-28_21-33-52.png

这里调用了runCLI的引用来自bootstrap这个文件,我们来看下这个文件

3. webpack-cli/lib/bootstrap.js

Snipaste_2022-03-28_21-36-15.png

1. 创建了WebpackCLI对象

2. 执行cli.run

而这个WebpackCLI来自webpack-cli.js

4.webpack-cli/lib/webpack-cli.js

run方法这里主要是执行了以下流程

1. 执行makeCommand

为了检查一些依赖包是否存在

Snipaste_2022-03-28_21-41-53.png

2. 执行makeOption

makeCommand方法里面执行makeOption方法,对我们传入的参数做了进一步处理

Snipaste_2022-03-28_21-46-20.png

3. 执行runWebpack

4. 执行createCompiler

而在runWebpack里面主要执行了createCompiler

Snipaste_2022-03-28_21-47-32.png

5. 执行webpack

而在createCompiler里面主要调用webpack这个方法,而这个webpack方法就是来自webpack包

Snipaste_2022-03-28_21-50-13.png

到这一步其实webpack已经打包完成了

webpack Compiler创建原理

在上述执行webpack函数创建了compiler,那这个是compiler是如何创建的呢,我们来看一下这个webpack方法。webpack来自webpack/lib/webpack.js

webpack/lib/webpack.js

Snipaste_2022-03-28_22-40-44.png

在wepack方法里面可以看到,不管是否有回调都会调用create返回compiler

1. 执行create

create方法中主要执行了createCompiler创建了compiler

Snipaste_2022-03-28_22-48-19.png

2. 执行createCompiler

Snipaste_2022-03-28_22-52-45.png

而在createCompiler主要做了

  1. new了一个Compiler对象
  2. plugin.apply注册了所有的插件
  3. 调用了environmentafterEnvironment环境hook
  4. 调用new WebpackOptionsApply().process将配置属性转为plugin注册
  5. 返回compiler

3. 执行compiler.run

webpack方法内首先会判断是否有callback回调,如果存在回调会执行compiler.run,如果不存在直接返回compiler,所以我们在外面在执行webpack方法获取compiler后,我们即可以传入一个回调方法,也可以调用run方法。

webpack/lib/WebpackOptionsApply.js

1. 插件注入plugin.apply()

webpack中的createCompiler里我们调用了new WebpackOptionsApply().process,我们来看看这里到底怎么实现将配置属性转为plugin注册

Snipaste_2022-03-28_23-07-26.png

其实在process方法中,我们将传入的属性转成webpackplugin注入到webpack生命周期内,如上图展示的部分属性做判断,存在就将内置的Plugin进行导入(所以plugin事实上贯穿webpack的整个构建流程),其实这个方法都是在做plugin.apply的调用注册,并将compiler对象传入进去,这些Plugin后续会通过tapable来实现钩子的监听, 并进行自己的处理逻辑

Compiler中run方法执行原理

webpack/lib/Compiler.js

在上述createCompiler中我们new了一个Compiler对象,这个构造方法主要做了什么呢,我们可以看下webpack/lib/Compiler.js这个文件

Snipaste_2022-03-28_23-16-08.png

new Compiler这个构造函数是会初始化各种各样的hooks,而之前说process里面的plugin里会注册这些hooks,这些hooks来自一个叫tapable的库来管理的,这是由webpack 官方自己来维护的一个库,对于tapable这个库的介绍使用可以看我另一篇webpack文章webpack5之Loader和Plugin的实现

现在我们来看看Compiler内的run方法,其实主要是执行之前plugin注册的hooks

Snipaste_2022-03-28_23-29-53.png

而在Compiler里面的run方法里,又定义了一个run方法,那我们看下这里做了什么

1. 执行run

  1. 首先执行了hooks.beforeRun,执行一些需要运行前操作的plugin
  2. 再执行了hooks.run,执行一些需要运行开始需要操作的plugin
  3. 执行compile方法,并传入了onCompiled编译完成的回调

2. 执行compile

当执行到this.compile就是开始准备编译了,我们来看看compile里面做了什么

Snipaste_2022-03-28_23-44-45.png

  1. 执行hooks.beforeCompile
  2. 执行hooks.compile
  3. 执行hooks.make
  4. 执行hooks.finishMake
  5. 执行hooks.afterCompile

其实hooks.make是最终的编译过程,而在hooks.compilehooks.make之间执行了const compilation = this.newCompilation(params);,并将compilation传入了hooks.make

这里的Compilation与Compiler有什么区别呢

Compiler

  • 在webpack构建的之初就会创建的一个对象, 并且在webpack的整个生命周期都会存在(before - run - beforeCompiler - compile - make - finishMake - afterCompiler - done)
  • 只要是做webpack的编译, 都会先创建一个Compiler
  • 如果修改webpack配置需要重新npm run build

Compilation

  • 存在于compile - make阶段
  • watch源代码,每次发生改变就需要重新编译模块,创建一个新的Compilation对象

Compilation对Module的处理

上述的hooks.make只是一个hook的调用,我们要去找注册在这个钩子上的回调,我们可以前往process内的new EntryOptionPlugin().apply(compiler) 这个entry插件

1. webpack/lib/EntryPlugin.js

Snipaste_2022-03-29_00-07-59.png

这个插件在apply里调用applyEntryOption,而里面又调用EntryPlugin插件

Snipaste_2022-03-29_00-11-47.png

EntryPlugin插件内可以看到注册了hooks.make

Snipaste_2022-03-29_00-12-39.png

而在注册回调中主要执行了compilation.addEntry,那我们来看看在compilation这个对象中主要做了什么

2. webpack/lib/Compilation.js

Snipaste_2022-03-29_00-24-23.png

在执行compilation.addEntry这里主要做了

  1. 执行_addEntryItem,用于添加入口的Item
  2. 执行addModuleTree
  3. addModuleTree中执行handleModuleCreation
  4. handleModuleCreation中执行factorizeModule,添加hooksfactorizeQueue队列中
  5. handleModuleCreation中执行addModule,添加module模块到addModuleQueue队列中
  6. addModule中执行buildModule,将需要构建的module模块添加到buildQueue队列中
  7. buildQueue队列中有一个processor属性,执行_buildModule
  8. _buildModule中执行module.needBuild判断模块是否需要构建
  9. 执行module.build
  10. 最后会在wepack/lib/NormalModule.js中执行build方法,开始构建模块

Snipaste_2022-03-29_00-44-51.png

module的build阶段

上面在处理module的最后在wepack/lib/NormalModule.js中执行build方法,开始构建模块,那现在我们来看看build做了哪些内容

wepack/lib/NormalModule.js

1. 执行doBuild

Snipaste_2022-03-29_11-21-45.png

2. 执行_doBuild

执行doBuild内的_doBuild方法

Snipaste_2022-03-29_11-18-33.png

3. 执行runLoaders

执行_doBuildrunLoaders,这个runLoaders来自独立的loader-runner库,我们之前配置的各种Loaders就是在这里处理的

4. 执行processResult

runLoaders执行结束后回执行processResult这个回调

Snipaste_2022-03-29_11-21-45.png

5. 执行parse

之后会调用parse解析AST

Snipaste_2022-03-29_11-33-12.png

而这个parse来自webpack/lib/javascript/JavascriptParser.js内的parse

Snipaste_2022-03-29_11-40-25.png

这个parse其实是用到了acorn这个库来解析javascript

Snipaste_2022-03-29_11-42-02.png

6. 执行handleBuildDone

解析完后会调用handleParseResult回调,里面执行handleBuildDone

Snipaste_2022-03-29_11-44-42.png

handleBuildDone里又执行了build里面传进来的回调

Snipaste_2022-03-29_11-44-42.png

Snipaste_2022-03-29_11-54-59.png

最终执行的webpack/lib/Compilation.js下的module.build传进来的回调

Snipaste_2022-03-29_11-55-52.png

7. _buildModule执行完成

_buildModule执行完成后,最终hooks.make执行完成,于是接下来会执行webpack/lib/Compiler.jscompilation.finishcompilation.seal方法

Snipaste_2022-03-29_14-18-06.png

seal这一步,就是开始将静态资源输出到构建目录了

输出静态资源到构建目录

1. 执行hooks.optimizeChunkModules

Exécutez d'abord hooks.optimizeChunkModules, optimisez le code du module avant

Snipaste_2022-03-29_15-33-03.png

2. Exécutez la génération de code

exécuter codeGeneration, générer du code

Snipaste_2022-03-29_15-34-24.png

3. Exécutez createChunkAssets

exécuter createChunkAssets, créer une chunkAssetsressource

Snipaste_2022-03-29_15-38-32.png

4. Exécutez getRenderManifest

Dans l'exécution createChunkAssets, getRenderManifestplacez toutes les données dans un objet manifeste

Snipaste_2022-03-29_15-38-59.png

5. Exécutez l'emitAsset

Execute emitAsset, output resources, à ce moment les ressources ont été stockées en mémoire

Snipaste_2022-03-29_15-41-50.png

6. Exécuter sur compilé

webpack/lib/CompilerExécuter le rappel après l' compileachèvement finalonCompiled

Snipaste_2022-03-29_15-43-54.png

7. Exécutez l'émission d'assets

onCompiledExécuter dans le callback emitAssets,

Snipaste_2022-03-29_15-45-53.png

8. Exécutez hooks.emit

emitAssetsEnfin , exécutez hooks.emitl'exportation de la ressource vers le répertoire de construction à l'intérieur

Snipaste_2022-03-29_15-47-28.png

finir

L'introduction du code source peut encore être incomplète. Lors de la visualisation du code source, nous pouvons utiliser l'outil de débogage de vscode pour afficher la direction du code à travers les points d'arrêt. L'introduction ci-dessus devrait pouvoir clarifier grossièrement le processus d'exécution du webpack, mais plus Pour les détails, j'espère toujours que vous pourrez explorer davantage avec le débogueur.

Je suppose que tu aimes

Origine juejin.im/post/7080447622300827655
conseillé
Classement