Webpack Compiler 源码全面解析

Compiler

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

类图解析:

1. Tapable 基类

Webpack 插件系统的核心,提供钩子注册(plugin)和触发(applyPlugins)能力。CompilerCompilation 均继承此类,支持插件通过生命周期钩子介入构建流程。

2. Compiler 类

在这里插入图片描述
在这里插入图片描述

• 核心属性

 ◦ `options`:整合 Webpack 配置(入口、出口、Loader 等)  ◦ `hooks`:包含 `run`(构建启动)、`compile`(编译开始)、`emit`(资源生成前)等钩子,插件可监听这些事件  

• 核心方法

 ◦ `run()`:启动构建流程,触发 `beforeRun` 和 `run` 钩子  ◦ `compile()`:创建 `Compilation` 实例,进入模块解析阶段

3. Compilation 类

在这里插入图片描述

• 核心属性

 ◦ `modules`:所有被处理的模块集合,包含源码和依赖信息  ◦ `chunks`:代码分块(如通过 `SplitChunksPlugin` 分割的公共模块)  ◦ `assets`:最终输出的文件内容(如 JS、CSS、图片等)  

• 核心方法

 ◦ `addEntry()`:从入口文件递归分析依赖,构建模块依赖图  ◦ `seal()`:冻结依赖图,执行 Tree Shaking 和代码压缩等优化  ◦ `emitAsset()`:将资源写入磁盘,触发 `emit` 钩子

4 协作关系

• 生命周期:Compiler 管理全局构建流程(如初始化配置、触发钩子),而 Compilation 负责单次编译的具体实现(模块解析、优化、输出)

• 实例化:每次构建(包括开发模式下文件变化)时,Compiler 会创建新的 Compilation 实例,确保资源状态隔离。

应用场景示例:
• 插件开发:通过监听 Compiler.hooks.emit 修改输出内容(如删除注释)

• 性能优化:利用 Compilation.modules 分析模块体积,实现按需加载。

在这里插入图片描述


在前端工程化中,自定义 Webpack 的 Loader 和 Plugin 是扩展构建流程的核心能力。以下从实现原理、开发步骤、典型场景等维度深入解析两者的设计与应用:


自定义loader和plugin

一、自定义 Loader 的实现

1. 核心原理与开发步骤

• 本质与作用

Loader 是文件转换器,将非 JS 文件(如 Markdown、CSS)转换为 Webpack 可处理的模块。其开发需遵循单一职责原则,且需保持无状态。

• 实现步骤:

  1. 创建函数:导出一个处理文件内容的函数,接收 source(文件内容)作为输入。
  2. 处理内容:通过正则或工具库(如 markedbabel)对内容转换,例如将 Markdown 转 HTML。
  3. 返回结果:需返回 JS 代码字符串,支持 module.exports 或 ES Modules 导出。
  4. 配置使用:在 webpack.config.jsmodule.rules 中通过 test 匹配文件类型并串联 Loader。
2. 同步与异步 Loader

• 同步处理:直接返回结果,适用于简单转换(如字符串替换)。

module.exports = function (content) {return content.replace(/world/g, 'loader'); // 替换文本
};

• 异步处理:通过 this.async() 实现异步操作(如网络请求、文件读取)。

module.exports = function (content) {const callback = this.async();fetchData().then(() => callback(null, processedContent));
};
3. 典型场景示例

• 多语言翻译:替换代码中的 __t('KEY') 为对应语言字符串。

• 资源优化:使用 svgo 压缩 SVG 文件,或通过 imagemin 生成 WebP 图片。

• 语法转换:自定义 Babel Loader 实现 ES6 转 ES5。


二、自定义 Plugin 的实现

在这里插入图片描述

1. 核心机制与生命周期

• 实现原理:

Plugin 通过监听 Webpack 生命周期钩子(如 emitdone)介入构建流程,操作 compilercompilation 对象。

• 开发步骤:

  1. 创建类:定义包含 apply 方法的类,接收 compiler 对象。
  2. 注册钩子:在目标钩子(如 emit)中挂载逻辑,操作资源或生成附加文件。
  3. 配置使用:在 plugins 数组中实例化插件。
2. 典型场景示例

• 打包报告生成:在 done 钩子中生成包含构建时间、模块大小的 JSON 报告。

• 资源修改:在 emit 阶段遍历 compilation.assets,删除 JS 注释或修改文件内容。

compiler.hooks.emit.tap('MyPlugin', (compilation) => {Object.keys(compilation.assets).forEach(name => {if (name.endsWith('.js')) {const content = compilation.assets[name].source().replace(/\/\*.*?\*\//g, '');compilation.assets[name] = { source: () => content, size: () => content.length };}});
});

• 自动化注入:类似 HtmlWebpackPlugin,动态生成 HTML 并插入脚本。

3. 高级应用

• 自定义钩子:通过 tapable 创建同步/异步钩子,扩展插件间的通信能力。

• 多插件协作:结合其他插件(如 CleanWebpackPlugin)清理构建目录。


三、Loader 与 Plugin 的协同与对比

维度LoaderPlugin
作用层级单文件处理(如转译、压缩)全局流程控制(如资源优化、报告生成)
执行时机模块加载阶段任意构建阶段(通过钩子介入)
配置方式module.rules 中定义规则链plugins 数组实例化
典型工具babel-loadercss-loaderHtmlWebpackPluginTerserPlugin

四、调试与优化建议

  1. Loader 调试
    • 使用 loader-runner 独立测试逻辑。

    • 通过 this.getOptions() 获取配置参数,结合 schema.json 校验参数合法性。

  2. Plugin 性能优化
    • 在 afterEmit 阶段执行耗时操作,避免阻塞主流程。

    • 利用 compilation.fileTimestamps 缓存文件修改时间,减少重复处理。


五、总结

自定义 Loader 和 Plugin 是 Webpack 生态灵活性的核心体现。Loader 聚焦于文件级转换,适合语法兼容、资源预处理等场景;Plugin 则通过生命周期钩子实现全局控制,适用于构建优化、自动化注入等复杂需求。两者的协同使用可覆盖从模块处理到工程化优化的全链路需求,开发者可根据具体场景选择合适方案。


  1. 自定义 Loader:将 Markdown 转换为 HTML。
  2. 自定义 Plugin:构建结束发送通知(以控制台模拟为例,实际可扩展为系统通知)。
  3. 自定义 Plugin:构建时检测重复依赖并输出警告。

样例

🔧 1. 自定义 Markdown 转 HTML Loader

依赖:安装 marked(或 markdown-it

npm install marked --save-dev
loaders/md-to-html-loader.js
const marked = require('marked');module.exports = function (source) {const html = marked(source);// 返回一段 JS 模块代码,导出 HTML 字符串return `export default ${JSON.stringify(html)}`;
};
webpack.config.js 中配置:
module.exports = {module: {rules: [{test: /\.md$/,use: path.resolve(__dirname, 'loaders/md-to-html-loader.js')}]}
};

🔔 2. 自定义构建结束发送通知 Plugin

控制台通知实现(也可以结合 node-notifier 发桌面通知)

plugins/build-notifier-plugin.js
class BuildNotifierPlugin {apply(compiler) {compiler.hooks.done.tap('BuildNotifierPlugin', (stats) => {const time = (stats.endTime - stats.startTime) / 1000;console.log(`✅ 构建完成!耗时 ${time.toFixed(2)}`);});}
}module.exports = BuildNotifierPlugin;
webpack.config.js 中配置:
const BuildNotifierPlugin = require('./plugins/build-notifier-plugin');module.exports = {plugins: [new BuildNotifierPlugin()]
};

可选增强:使用 node-notifier 发系统弹窗提示。


🧩 3. 自定义重复依赖检测 Plugin

这个插件会分析所有模块中使用的依赖包并查找是否存在多个版本的情况(如多个 lodash)

plugins/duplicate-dependency-plugin.js
const path = require('path');
const fs = require('fs');class DuplicateDependencyPlugin {apply(compiler) {compiler.hooks.emit.tapAsync('DuplicateDependencyPlugin', (compilation, callback) => {const moduleVersions = {};compilation.modules.forEach((module) => {if (module.resource && module.resource.includes('node_modules')) {const parts = module.resource.split('node_modules' + path.sep);if (parts[1]) {const pkgPath = parts[1].split(path.sep);const name = pkgPath[0].startsWith('@') ? `${pkgPath[0]}/${pkgPath[1]}` : pkgPath[0];const packageJsonPath = path.join(module.resource.split('node_modules')[0], 'node_modules', name, 'package.json');try {const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));if (!moduleVersions[name]) {moduleVersions[name] = new Set();}moduleVersions[name].add(pkg.version);} catch (err) {// 忽略找不到 package.json 的模块}}}});// 输出重复依赖警告Object.entries(moduleVersions).forEach(([name, versions]) => {if (versions.size > 1) {console.warn(`⚠️ 发现重复依赖:${name},版本有:${[...versions].join(', ')}`);}});callback();});}
}module.exports = DuplicateDependencyPlugin;
webpack.config.js 中配置:
const DuplicateDependencyPlugin = require('./plugins/duplicate-dependency-plugin');module.exports = {plugins: [new DuplicateDependencyPlugin()]
};

📦 最终项目结构参考

webpack-project/
├── loaders/
│   └── md-to-html-loader.js
├── plugins/
│   ├── build-notifier-plugin.js
│   └── duplicate-dependency-plugin.js
├── src/
│   └── index.js
├── content/
│   └── example.md
├── webpack.config.js
└── package.json

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/79519.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/79519.shtml
英文地址,请注明出处:http://en.pswp.cn/web/79519.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HAProxy + Keepalived + Nginx 高可用负载均衡系统

1. 项目背景 在现代Web应用中,高可用性和负载均衡是两个至关重要的需求。本项目旨在通过HAProxy实现流量分发,通过Keepalived实现高可用性,通过Nginx提供后端服务。该架构能够确保在单点故障的情况下,系统仍然能够正常运行&#…

Kubernetes控制平面组件:Kubelet详解(一):API接口层介绍

云原生学习路线导航页(持续更新中) kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计(一)Kubernetes架构原则和对象设计(二)Kubernetes架构原则和对象设计(三)Kubernetes控…

VIC-2D 7.0 为平面样件机械试验提供全视野位移及应变数据软件

The VIC-2D系统是一个完全集成的解决方案,它基于优化的相关算法为平面试样的力学测试提供非接触、全场的二维位移和应变数据,可测量关注区域内的每个像素子集的面内位移,并通过多种张量选项计算全场应变。The VIC-2D 系统可测量超过 2000%变形…

多线程访问Servlet如何谨慎处理共享资源

1. 避免共享状态(最佳实践) 核心思想:Servlet 本身应设计为无状态(Stateless),不依赖实例变量存储请求相关数据。 实现方式: 将变量声明在方法内部(局部变量)&#xff0…

从Windows到Mac的过渡:学习笔记与心得

作为一名长期使用Windows操作系统的用户,当我决定转换到Mac时,心中充满了期待与好奇。Mac以其独特的操作系统和设计风格著称,虽然有许多相似之处,但仍有不少差异需要适应。为了帮助其他有类似转换需求的朋友,我总结了一…

TestNG接口自动化

第一章、 Rest assured接口测试框架 一、概述 接口自动化的框架,主要是用来做接口自动化测试,返回的报文都是JSON 语法比较简单,只需要掌握常用的方法 用例运行的速度非常快 断言的机制 Json 封装相关方法,jsonpath,x…

【速写】KV-cache与解码的再探讨(以束搜索实现为例)

文章目录 1 Beam Search 解码算法实现2 实现带KV Cache的Beam Search解码3 关于在带kv-cache的情况下的use_cache参数 1 Beam Search 解码算法实现 下面是一个使用PyTorch实现的beam search解码算法: 几个小细节: 束搜索可以加入length_penalty&#…

ABP-Book Store Application中文讲解 - 前期准备 - Part 3:Acme.BookStore项目模块详解之二

1. 汇总 ABP-Book Store Application中文讲解-汇总-CSDN博客 2. 前一章 ABP-Book Store Application中文讲解 - 前期准备 - Part 3:Acme.BookStore项目模块详解 项目之间的引用关系。 目录 1. .Domain.Shared 2. .Domain 3. .Application.Contracts 4. .Application 5…

【Leetcode刷题随笔】349. 两个数组的交集

1. 题目描述 给定两个数组nums1和nums2&#xff0c;返回它们的交集。输出结果中的每个元素一定是唯一的。我们可以不考虑输出结果的顺序。 示例1: 输入:nums1 [1,2,2,1], nums2 [2,2] 输出&#xff1a;[2] 题目条件&#xff1a; 1 < nums1.length, nums2.length < 10…

Unity打包安卓失败 Build failure 解决方法

【Unity】打包安卓失败 Build failure 的解决方法_com.android.build.gradle.internal.res.linkapplicat-CSDN博客 unity在打包时设置手机屏幕横屏竖屏的方法_unity打包默认横屏-CSDN博客

Window、CentOs、Ubuntu 安装 docker

Window 版本 网址&#xff1a;https://www.docker.com/ 下载 下载完成后&#xff0c;双击安装就可以了 Centos 版本 卸载 Docker &#xff08;可选&#xff09; yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-log…

Matlab自学笔记五十四:符号数学工具箱和符号运算、符号求解、绘图

1.什么是符号数学工具箱&#xff1f; 符号数学工具箱是Matlab针对符号对象的运算功能&#xff0c;它引入了一种特殊的数据类型 - 符号对象&#xff1b; 该数据类型包括符号数字&#xff0c;符号变量&#xff0c;符号表达式和符号函数&#xff0c;还包含符号矩阵&#xff0c;以…

OpenCV进阶操作:图像的透视变换

文章目录 前言一、什么是透视变换&#xff1f;二、透视变换的过程三、OpenCV透视变换核心函数四、文档扫描校正&#xff08;代码&#xff09;1、预处理2、定义轮廓点的排序函数3、定义透视变换函数4、读取原图并缩放5、轮廓检测6、绘制最大轮廓7、对最大轮廓进行透视变换8、旋转…

【python】基础知识点100问

以下是Python基础语法知识的30条要点整理,涵盖数据类型、函数、控制结构等核心内容,结合最新资料归纳总结: 基础30问 一、函数特性 函数多返回值 支持用逗号分隔返回多个值,自动打包为元组,接收时可解包到多个变量 def func(): return 1, "a" x, y = func()匿…

采用AI神经网络降噪算法的通信语音降噪(ENC)模组性能测试和应用

采用AI降噪的语言通话环境抑制模组性能效果测试 随着AI时代来临.通话设备的环境噪音抑制也进入AI降噪算法时代. AI神经网络降噪技术是一款革命性的语音处理技术&#xff0c;他突破了传统单麦克风和双麦克风降噪的局限性,利用采集的各种日常环境中的噪音样本进行训练学习.让降噪…

openwrt目录结构(部分)

1&#xff0c;openwrt 原始目录需要注意的目录 tools: 该目录下存放着一些&#xff0c;编译工程的自动化工具包和一些在编译过程用到的命令包&#xff0c; 查看目录下的Makefile&#xff0c;知道其会在编译过程中将依赖包下载 例如&#xff1a; autoconf / lzma / mkimage/ …

RDB和AOF的区别

Redis提供两种主要的持久化机制&#xff1a;RDB&#xff08;Redis Database&#xff09;和AOF&#xff08;Append Only File&#xff09;&#xff0c;它们在数据持久化方式、性能影响及恢复策略上各有特点。以下是两者的对比分析及使用建议&#xff1a; RDB&#xff08;快照持久…

基于大模型的甲状腺结节诊疗全流程预测与方案研究报告

目录 一、引言 1.1 研究背景与目的 1.2 研究意义 1.3 国内外研究现状 二、大模型预测原理与方法 2.1 相关大模型概述 2.2 数据收集与预处理 2.3 模型训练与验证 三、术前预测与评估 3.1 结节性质预测 3.1.1 良恶性判断 3.1.2 与传统诊断方法对比 3.2 手术风险预测…

逆向破解:x64dbg

文章目录 一、CPU窗口1、反汇编窗口2、寄存器窗口3、栈地址窗口4、十六进制数据窗口5、堆栈参数解析窗口 二、常用快捷键三、字符串检索功能四、调试功能1、上一步 一、CPU窗口 1、反汇编窗口 2、寄存器窗口 寄存器窗口用于显示和解释当前线程环境下CPU寄存器的各种状态值和内…

免布线视频桩如何重塑停车管理模式

传统停车管理常因布线复杂、维护成本高而难以推广&#xff0c;而“免布线视频桩”通过无线设计、低功耗与高精度检测&#xff0c;为城市停车提供高效解决方案。作为智慧城市建设的创新工具&#xff0c;免布线视频桩以即装即用、长效续航等特性&#xff0c;正在重塑停车管理模式…