浏览源码先导

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__等。

webpack的很多代码的运行基于是否有配置,因为只是为了理解打包过程与不便调试,所以后续代码示例大部分会去掉一些无用代码,错误处理,为了实现cache的代码等。并且运行模式为"development",在"production"下很多配置会默认为true,会对代码压缩,不利于了解。