웹팩 시리즈 디렉토리
- webpack5의 핵심 구성
- webpack5의 모듈 원리
- webpack5의 Babel/ESlint/브라우저 호환성
- webpack5의 성능 최적화
- webpack5의 Loader 및 Plugin 구현
- webpack5의 핵심 소스 코드 분석
webpack 명령 시작의 원리
우리는 일반적으로 빌드 패키징 명령과 같은 npm 스크립트에서 webpack 명령을 구성하여 webpack을 시작합니다.
"scripts" {
"build": "webpack --config ./config/webpack.common.js --env production"
}
复制代码
1. webpack/bin/webpack.js
webpack 명령을 사용하여 webpack-cli
도구의 도움으로 시작합니다.webpack을 설치할 때 bin
webpack의 package.json에 속성 줄이 있고 npm은 bin
내부 속성 이름 webpack
을 파일 이름으로 사용하고 이 파일의 새 내용을 사용합니다. bin
디렉토리webpack.js
에 content.file로 복사 node_modules/.bin
하여 아래에 설치
따라서 webpack 명령을 실행할 때 실제로 webpack/bin/webpack.js
이 파일을 실행합니다.
이 파일 을 살펴보겠습니다 webpack/bin/webpack.js
. 주요 프로세스는 다음과 같습니다.
- 먼저
cli
객체 정의 cli
내부installed
속성이 참 인지 판단할 때- 구현하다
runCli
즉, 먼저 webpack-cli
패키지가 설치되어 있는지 여부를 확인합니다. 그렇지 않으면 조건부 설치로 들어가고 그렇지 않으면 실행 runCli
됩니다. 일반적으로 패키지 명령을 사용 하여 두 패키지를 다음 위치 에 설치 webpack
및 설치합니다. webpack-cli
동시에, 그래서 가장 중요한 것은 보는 것입니다runCli
1. runCli 실행
runcli에서 가장 중요한 것은 cli의 속성 값을 경로, pkgPath
즉 webpack-cli/package.json
, 객체 로 연결 pkg
하는 것이며 cli에 있는 속성 의 해당 속성 값은webpack-cli/package.json
webpack-cli/package.json
bin
webpack-cli
bin/cli.js
所以最后拼接的是webpack-cli/bin/cli.js
这个路径,将调用这个文件
2. webpack-cli/bin/cli.js
1. 执行runCLI
这个文件其实主要执行的是runCLI
,并且将命令后的参数一起传进去
这里调用了runCLI
的引用来自bootstrap这个文件,我们来看下这个文件
3. webpack-cli/lib/bootstrap.js
1. 创建了WebpackCLI对象
2. 执行cli.run
而这个WebpackCLI
来自webpack-cli.js
4.webpack-cli/lib/webpack-cli.js
run
方法这里主要是执行了以下流程
1. 执行makeCommand
为了检查一些依赖包是否存在
2. 执行makeOption
makeCommand
方法里面执行makeOption
方法,对我们传入的参数做了进一步处理
3. 执行runWebpack
4. 执行createCompiler
而在runWebpack
里面主要执行了createCompiler
5. 执行webpack
而在createCompiler
里面主要调用webpack
这个方法,而这个webpack
方法就是来自webpack包
到这一步其实webpack已经打包完成了
webpack Compiler创建原理
在上述执行webpack函数创建了compiler
,那这个是compiler
是如何创建的呢,我们来看一下这个webpack方法。webpack来自webpack/lib/webpack.js
webpack/lib/webpack.js
在wepack方法里面可以看到,不管是否有回调都会调用create
返回compiler
1. 执行create
在create
方法中主要执行了createCompiler
创建了compiler
2. 执行createCompiler
而在createCompiler
主要做了
new
了一个Compiler
对象plugin.apply
注册了所有的插件- 调用了
environment
和afterEnvironment
环境hook
- 调用
new WebpackOptionsApply().process
将配置属性转为plugin
注册 - 返回
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
注册
其实在process
方法中,我们将传入的属性转成webpack
的plugin
注入到webpack生命周期内,如上图展示的部分属性做判断,存在就将内置的Plugin进行导入(所以plugin事实上贯穿webpack的整个构建流程),其实这个方法都是在做plugin.apply
的调用注册,并将compiler
对象传入进去,这些Plugin后续会通过tapable
来实现钩子的监听, 并进行自己的处理逻辑
Compiler中run方法执行原理
webpack/lib/Compiler.js
在上述createCompiler
中我们new
了一个Compiler
对象,这个构造方法主要做了什么呢,我们可以看下webpack/lib/Compiler.js
这个文件
当new Compiler
这个构造函数是会初始化各种各样的hooks
,而之前说process
里面的plugin
里会注册这些hooks
,这些hooks
来自一个叫tapable
的库来管理的,这是由webpack
官方自己来维护的一个库,对于tapable
这个库的介绍使用可以看我另一篇webpack文章webpack5之Loader和Plugin的实现。
现在我们来看看Compiler
内的run
方法,其实主要是执行之前plugin
注册的hooks
。
而在Compiler
里面的run
方法里,又定义了一个run
方法,那我们看下这里做了什么
1. 执行run
- 首先执行了
hooks.beforeRun
,执行一些需要运行前操作的plugin
- 再执行了
hooks.run
,执行一些需要运行开始需要操作的plugin
- 执行
compile
方法,并传入了onCompiled
编译完成的回调
2. 执行compile
当执行到this.compile
就是开始准备编译了,我们来看看compile
里面做了什么
- 执行
hooks.beforeCompile
- 执行
hooks.compile
- 执行
hooks.make
- 执行
hooks.finishMake
- 执行
hooks.afterCompile
其实hooks.make
是最终的编译过程,而在hooks.compile
和hooks.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
这个插件在apply
里调用applyEntryOption
,而里面又调用EntryPlugin
插件
EntryPlugin
插件内可以看到注册了hooks.make
而在注册回调中主要执行了compilation.addEntry
,那我们来看看在compilation
这个对象中主要做了什么
2. webpack/lib/Compilation.js
在执行compilation.addEntry
这里主要做了
- 执行
_addEntryItem
,用于添加入口的Item - 执行
addModuleTree
- 在
addModuleTree
中执行handleModuleCreation
- 在
handleModuleCreation
中执行factorizeModule
,添加hooks
到factorizeQueue
队列中 - 在
handleModuleCreation
中执行addModule
,添加module
模块到addModuleQueue
队列中 - 在
addModule
中执行buildModule
,将需要构建的module
模块添加到buildQueue
队列中 buildQueue
队列中有一个processor
属性,执行_buildModule
_buildModule
中执行module.needBuild
判断模块是否需要构建- 执行
module.build
, - 最后会在
wepack/lib/NormalModule.js
中执行build
方法,开始构建模块
module的build阶段
上面在处理module
的最后在wepack/lib/NormalModule.js
中执行build
方法,开始构建模块,那现在我们来看看build
做了哪些内容
wepack/lib/NormalModule.js
1. 执行doBuild
2. 执行_doBuild
执行doBuild
内的_doBuild
方法
3. 执行runLoaders
执行_doBuild
内runLoaders
,这个runLoaders
来自独立的loader-runner
库,我们之前配置的各种Loaders就是在这里处理的
4. 执行processResult
runLoaders
执行结束后回执行processResult
这个回调
5. 执行parse
之后会调用parse
解析AST
树
而这个parse
来自webpack/lib/javascript/JavascriptParser.js
内的parse
这个parse
其实是用到了acorn
这个库来解析javascript
6. 执行handleBuildDone
解析完后会调用handleParseResult
回调,里面执行handleBuildDone
handleBuildDone
里又执行了build
里面传进来的回调
最终执行的webpack/lib/Compilation.js
下的module.build
传进来的回调
7. _buildModule执行完成
当_buildModule
执行完成后,最终hooks.make
执行完成,于是接下来会执行webpack/lib/Compiler.js
的compilation.finish
和compilation.seal
方法
到seal
这一步,就是开始将静态资源输出到构建目录了
输出静态资源到构建目录
1. 执行hooks.optimizeChunkModules
먼저 실행 hooks.optimizeChunkModules
하고 그 전에 모듈 코드를 최적화하십시오.
2. codeGeneration 실행
실행 codeGeneration
, 코드 생성
3. createChunkAssets 실행
실행 createChunkAssets
, chunkAssets
리소스 생성
4. getRenderManifest 실행
실행 createChunkAssets
에서 getRenderManifest
모든 데이터를 매니페스트 개체에 넣습니다.
5. 이미터 자산 실행
Execute emitAsset
, 출력 자원, 이때 자원은 메모리에 저장되었습니다.
6. onCompiled 실행
최종 완료 webpack/lib/Compiler
후 콜백 실행compile
onCompiled
7. 방출 자산 실행
onCompiled
콜백에서 실행 emitAssets
,
8. hooks.emit 실행
emitAssets
마지막으로 hooks.emit
내부의 빌드 디렉토리로 리소스 내보내기를 실행합니다.
끝
소스 코드의 도입이 아직 불완전할 수 있습니다. 소스 코드를 볼 때 vscode의 디버거 도구를 사용하여 중단점을 통해 코드 방향을 볼 수 있습니다. 위의 소개는 웹팩 실행 프로세스를 대략적으로 설명할 수 있을 것입니다. 자세한 내용은 디버거를 사용하여 더 많이 탐색할 수 있기를 바랍니다.