likes
comments
collection
share

图解webpack核心源码架构

作者站长头像
站长
· 阅读数 13

前言

大家好这里是阳九,一个被生活折磨的转码野路子码农.

最近还是想做一期对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主类负责协调各个模块之间的依赖关系,确保整个构建过程能够顺利地进行下去。

总架构图: 图解webpack核心源码架构


Compilation

Compilation指的是单个编译流程, 与Compiler是一对多的关系, 我们可以在Compiler中创建多个Compilation流程,

在该类里我们可以处理输入文件,处理后的输出代码、依赖项等。

compilation的整个构建流程,是通过tapable的hooks,流转各个不同的插件实现的 每个文件会通过ModuleFactory构建一个模块对象, 将这个模块对象传入整个构建流程中

  • 各种template : 用于创建代码,使用模板将模块的代码包裹成打包后生成的具体代码
  • moduleGraph: 模块之间的依赖图,描述了模块之间的依赖关系
  • moduleCache: 模块的缓存
  • ResolveFactory: 由compiler传入,用于创建resolver,以便ModuleFacory解析路径
  • ModuleFacory: 模块工厂, 会创建多个module实例,加入编译流程
  • Logger: 内置logger

单个Compilatio的结构

图解webpack核心源码架构


ModuleFactory与Hooks执行流程

模块工厂, 顾名思义,用于创建模块的工厂,

webpack中有多种不同的模块,不同类型的模块需要不同的处理方式,因此webpack为每一种类型的模块都提供了相应的工厂类。

  • NormalModule 普通模块
  • ContextModule 上下文模块
  • NullModule 空模块
  • 其他

所以我们也有不同的模块工厂, 其中最主要的就是NormalModuleFactory NormalModuleFactory主要做了如下几件事:

  1. 解析模块的依赖关系
  2. 将其转换为AST(抽象语法树)。
  3. 对AST进行各种编译操作(基于各种插件,配置来进行)

同样的,模块工厂的处理也是通过tapable进行流转,我们将一个文件解析包装成模块对象Module,

Module对象会记录当前模块的各种属性,如模块的路径、所属的 Chunk、代码内容等信息

创建时我们会将Module对象在各个hooks中的plugins中流转,

  • 每个Hook是一个生命周期阶段,内部提前注册了多个plugin插件

  • 每个Plugin都是一个函数,会对模块内容进行相应的处理, 并将模块对象流转到下一个plugins中

  • 大部分的插件都是webpack的内置插件,我们也可以通过配置,往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对象在各种插件中流转

图解webpack核心源码架构

关于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项目的搭建。