浏览源码先导
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__等。