图解webpack核心源码架构
前言
大家好这里是阳九,一个被生活折磨的转码野路子码农.
最近还是想做一期对webpack架构,代码分层的整体分析。
可能这篇写完就不再写webpack相关的东西了,也许不再研究前端了也说不定。
本人自己手写的webpack也在进行重构,尽量保持与源码结构和逻辑一致。
对webpack感兴趣的小伙伴可以看一下我的项目,比起硬啃webpack源码,根据我的简易手写版学习可能效率更高。
原生JS手写简易webpack+手写React测试项目 (github.com)
tapable与plugin
在说webpack源码之前,一定要对tapable有一个清楚的认知.
Tapable
是一个 JavaScript 库,用于支持在不同部分之间注入自定义功能的插件框架。
我们可以通过tapable在webpack的各个编译阶段插入不同的逻辑,
而这些逻辑被封装为了插件Plugins。实现在各个环节执行用户自定义的代码,
具体关于tapable的可以在各种博客论坛上搜索一下,到处都是。
对tapable源码感兴趣的同学也可以参考我的文章
webpack核心库 - tapable的设计思路与核心源码解析 - 掘金 (juejin.cn)
Compiler
webpack的主类, 在webpack在整个编译周期的启动阶段被创建, 并在整个编译生命周期中保持不变。 这个类负责读取配置文件、创建编译实例、启动编译过程等。
这个类中挂载了多个子类,比如
- Compilation: 单个编译流程
- ModuleFactory: 模块工厂
- ResolverFacory: 路径解析器工厂
- RequestShortener:路径简化器
- watcher: 文件监视系统
- FileSystem:各个文件系统
- cache: 缓存
compiler主类负责协调各个模块之间的依赖关系,确保整个构建过程能够顺利地进行下去。
总架构图:
Compilation
Compilation指的是单个编译流程, 与Compiler是一对多的关系, 我们可以在Compiler中创建多个Compilation流程,
在该类里我们可以处理输入文件,处理后的输出代码、依赖项等。
compilation的整个构建流程,是通过tapable的hooks,流转各个不同的插件实现的 每个文件会通过ModuleFactory构建一个模块对象, 将这个模块对象传入整个构建流程中
- 各种template : 用于创建代码,使用模板将模块的代码包裹成打包后生成的具体代码
- moduleGraph: 模块之间的依赖图,描述了模块之间的依赖关系
- moduleCache: 模块的缓存
- ResolveFactory: 由compiler传入,用于创建resolver,以便ModuleFacory解析路径
- ModuleFacory: 模块工厂, 会创建多个module实例,加入编译流程
- Logger: 内置logger
单个Compilatio的结构
ModuleFactory与Hooks执行流程
模块工厂, 顾名思义,用于创建模块的工厂,
webpack中有多种不同的模块,不同类型的模块需要不同的处理方式,因此webpack为每一种类型的模块都提供了相应的工厂类。
- NormalModule 普通模块
- ContextModule 上下文模块
- NullModule 空模块
- 其他
所以我们也有不同的模块工厂, 其中最主要的就是NormalModuleFactory NormalModuleFactory主要做了如下几件事:
- 解析模块的依赖关系
- 将其转换为AST(抽象语法树)。
- 对AST进行各种编译操作(基于各种插件,配置来进行)
同样的,模块工厂的处理也是通过tapable进行流转,我们将一个文件解析包装成模块对象Module,
Module对象会记录当前模块的各种属性,如模块的路径、所属的 Chunk、代码内容等信息
创建时我们会将Module对象在各个hooks中的plugins中流转,
-
每个Hook是一个生命周期阶段,内部提前注册了多个plugin插件
-
每个Plugin都是一个函数,会对模块内容进行相应的处理, 并将模块对象流转到下一个plugins中
-
大部分的插件都是webpack的内置插件,我们也可以通过配置,往webpack的某个生命周期钩子中添加自定义插件,修改代码内容
以下是一个模块的大致流转流程图
ResolverFactory
ResolverFactory
是 webpack 中用于创建 Resolver
实例的工厂类。核心逻辑由enhanced-resolve
库来进行实现,
Resolver
用于将模块请求(如 require/import
声明)解析为文件路径或模块ID。
例如我们写了require('./index'),resolver就会用它来解析出./index的完整路径。
const { ResolverFactory, CachedInputFileSystem } = require("enhanced-resolve");
const fs = require("fs");
const path = require("path");
const myResolver = ResolverFactory.createResolver({
fileSystem: new CachedInputFileSystem(fs, 4000),
extensions: [".json", ".js", ".ts"],
})
myResolver.resolve(path,request,context, (err, path, result) => {
// 接收到的result是 解析传入的path和request后的结果
console.log(result)
});
同样的, Resolver将传入的文件路径包装为一个request对象, 通过tapable将request对象在各种插件中流转
关于enhanced-resolve
,更详细的可参考我的文章
通过enhanced-resolve领略webpack的插件架构 - 掘金 (juejin.cn)
RequestShortener
RequestShortener
是Webpack中的一个辅助类,用于缩短和美化资源路径。
本质上没有实现什么业务逻辑,只是刚好看到了,就放出来讲讲。
在Webpack编译过程中,如果遇到了一些错误或警告,Webpack会抛出一些信息,这些信息通常包含了一些资源的路径信息,例如源码文件路径、依赖模块路径等。
这些路径信息往往比较长,使用起来不太方便
// 原路径
/root/project/src/components/Header/index.js
// 使用 `RequestShortener.shorten()` 方法可以将其转换为相对路径:
./components/Header/index.js
文件系统
webpack中一共挂载了四种文件系统
- inputFileSystem 缓存文件系统
- outputFileSystem 输出文件系统
- watchFileSystem 文件监控系统
- intermediateFileSystem 中间(件)文件系统
这四种文件系统各自提供了不同的功能,基本上都是由Node.js原生封装出来的系统。 可以直接看我的另一篇文章来了解这四种文件系统及其功能
细说webpack中的4种文件系统 - 掘金 (juejin.cn)
结尾
在重构完自己的webpack后,我希望能开源并宣传一下自己的webpack项目,
欢迎有兴趣的小伙伴联系我,一起参与mini-webpack项目的搭建。
转载自:https://juejin.cn/post/7224402365453074490