浏览源码先导
webpack源码很宏大,并且配置很丰盛,浏览起来十分困难。有很多重要的功能模块,概念,独立于webpack外的包等。在浏览源码之前最好先把它们独自整理出来进行简略的介绍,更利于前面的浏览。
Compiler
webpack在运行的时候,会传入配置信息,返回实例化的Compiler对象。它在运行环境里是惟一的,所有主流程都由它来管制,比方开始编译,从入口开始解决模块,对于不同模块依据配置进行封装在一个组里,最初生成文件等等。当然,它只是操作流程,流程内的事件都是托付给其余模块解决。
Compilation
它可能调用许多解决模块的办法,并保留相应的构建的信息,比方modules,assets,moduleGraph, chunkGraph等。每次资源的重构建都会产生新的Compilation。
Module
在webpack里,万物都是模块,js文件,图片,视频等。一般的模块都会生成NormalModule对象,它能够调用办法解析模块,能够调用parser解析成AST(形象语法树)等。NormalModule对象蕴含着Loader解决过后的代码,被哪个模块解析引入,模块解析的门路,引入其余模块的依赖等信息。而且在构建模块的时候,会生成moduleGraph来记录模块之间援用信息,能够保留导出信息,用于剖析导出是否有被应用。
Chunk
在构建完之后,如果不进行相干配置,所有的module都会被打包在一个Chunk里,但也能够自行配置,用最优的办法来进行代码分块,优化网页的加载速度。
Loader
webpack可能解决js,并且webpack5还增加了许多能解决其余资源的能力。然而还有很多文件webpack自身不能解决,须要引入其余Loader转换成js,才可能被webpack操作。如果你想写一个Loader解决相干资源,能够浏览文档
Plugin
插件可能帮忙webpack扩大性能,包含内置的很多性能也是通过插件的形式引入的,比方热模块替换,external,splitChunk等。它们通通扩大于Tapable,这样的益处在于可能很好的扩大webpack的性能并且和webpack解耦。如果你想写一个Plugin扩大性能,能够浏览这里
Parser
Parser用于解析资源,最罕用到的parser就是JavascriptParser。它用acorn将js解析成AST(形象语法树),并且对所有的申明或表达式进行遍历,找出相干模块的导入导出信息,增加至依赖里。也能收集导出导入状况,为后续的tree-shaking做筹备。也能解析正文,实现webpack magic comment性能。
除了JavascriptParser,还有webassemblyParser,会调用@webassemblyjs/ast
进行解析。还有cssParser , jsonParser等
独立库
除了webpack外围的代码,它还将很多性能独立成一个库,这些库不仅用于webpack,也能用于vanillaJs。
Tapable
这是webpack贯通全文的库,根本重要的功能模块都有它的身影,功能模块与plugin的通信都须要靠它。在webpack里它叫做hooks,其实就是一个高级的EventEmitter,我也写了一篇具体的文章去介绍它。
如果你想写好一个插件,最好理解webpack次要模块的hooks都有哪些:
- Compiler Hooks
- Compilation Hooks
- JavascriptParser Hooks
- NormalModuleFactory Hooks
enhanse-resolve
enhanse-resolve是一个高度可配置的resolve库,它在webpack用来解析文件和loader的门路信息,也能够反对许多配置,比方咱们最罕用的配置就是resolve.alias,通过别名更不便导入资源,还有resolve.extensions减少文件扩展名等,webpack resolve options都是用于配置这个库。
loader-runner
loader-runner用于执行webpack loader。
webpack-sources
webpack-sources能够生成源码的sourceMap,hash等
运行环境
webpack v5.70.0
webpack.config.js
const path = require("path");const { CleanWebpackPlugin } = require("clean-webpack-plugin");module.exports = { mode: "development", devtool: "source-map", entry: "./index.js", output: { path: path.join(__dirname, "./dist"), filename: "[fullhash].[name].bundle.js" }, optimization: { usedExports: true }, plugins: [new CleanWebpackPlugin()]};
index.js
import { c_var } from "./module_c"import(/* webpackExports: ["a"] */ "./module_a").then(({ a }) => { console.log(a)})var log_var = c_var;console.log(log_var)
module_c.js
const c_var = "module_c";const b_var = "unused";export { c_var, b_var };
module_a.js
export const a = "moudle_a";export const b = "unused";export { a, b };
入口为./index.js
全篇执行目录context为c:\Users\Administrator\Desktop\webpack
流程
webpack的流程能够分为四个大块来讲
run
初始化,做好开始编译的筹备工作。解决options,rules,注册插件,实例化compiler所须要的其余对象等。
make
通过入口文件开始构建模块,构建模块会分为几个步骤,解析门路,通过loader转换模块,进行paser收集依赖,递归解决所有内部依赖等。
seal
seal是属于Compilation的hooks,不像run,make,emit都属于Compiler,独自拿进去因为这个阶段也做了很多事,并且也是在make之后执行的。seal阶段不再接管模块,会依据ChunkGroup进行封装成一个或多个chunk,之后会做一些代码优化工作,生成文件Hash name等。
emit
输入文件阶段,webpack会依据不同的template来生成代码,一些代码会被替换,比方import,export会被替换成__webpack_require__,__webpack_exports__等。