当前位置: 首页 > news >正文

Webpack详解

Webpack详解

文章目录

  • Webpack详解
    • 一、配置
      • 1.Entry(入口)
      • 2. Output(输出)
      • 3. Loader(加载器)
      • 4. Plugin(插件)
      • 5. Module(模块)
      • 6. Dependency Graph(依赖图)
    • 二、Webpack 使用的模块规范
      • 1.AMD
      • 2.UMD(通用模块定义)
    • 三、Webpack 的构建流程(内部工作原理 / 打包过程)
        • 1.Tapable
          • 1.1 定义
          • 1.2 为什么 Webpack 需要 Tapable?
          • 1.3 Tapable 提供的钩子
          • 1.4 Tapable 是如何控制 Webpack 构建流程的?
    • 四、Webpack 的使用场景

一、配置

1.Entry(入口)

entry是 Webpack 构建的起点,即 Webpack 开始构建依赖图的第一个模块。从 entry指定的模块出发,Webpack 会递归地分析该模块所依赖的其他模块,最终构建出一个完整的依赖关系图。

作用:

  • 定义了 Webpack 打包的起始文件
  • 决定了依赖图的“根节点”
  • 通常对应应用的主 JS 文件,比如 index.jsapp.js

常见配置方式:

1.单入口(Single Entry)—— 字符串形式

适用场景: 项目只有一个入口文件,比如一个单页应用(SPA),只有一个 index.js作为主入口。

配置方式:

module.exports = {entry: './src/index.js', // 单入口:字符串形式
};

含义:

  • Webpack 会从 ./src/index.js开始分析依赖,构建依赖图。
  • 打包生成的文件通常也只有一个(除非你配置了多个 chunk,比如通过 SplitChunksPlugin)。

2.多入口(Multiple Entry)—— 对象形式(推荐用于多页面应用 / 多模块)

适用场景: 有多个入口文件,比如:

  • 多页面应用(MPA),每个页面有独立的 JS 入口,如 index.jsabout.jscontact.js
  • 应用中存在多个独立功能模块,需要分别打包

配置方式:

module.exports = {entry: {main: './src/index.js',about: './src/about.js',contact: './src/contact.js',},
};

含义:

  • 每个键值对代表一个入口点,键(如 mainabout)是 chunk 的名称值是对应的入口文件路径
  • Webpack 会为每个入口生成一个独立的依赖图,最终输出多个 bundle(通常也会配合多个 html 文件,比如通过 HtmlWebpackPlugin

输出示例(配合 output 配置):

output: {filename: '[name].bundle.js', // [name] 会被替换为 main、about、contactpath: path.resolve(__dirname, 'dist'),
}

最终会生成:main.bundle.js,about.bundle.js,contact.bundle.js


3.多入口 —— 数组形式(不常用,有特定用途)

配置方式:

module.exports = {entry: ['./src/index.js', './src/another.js'], // 数组形式
};

含义:

  • 这种写法是将多个文件合并为一个 chunk,即多个模块会一起打包进一个 bundle
  • Webpack 会把数组中的所有模块都作为入口模块的依赖,最终只生成一个 bundle
  • 通常用于:将多个依赖文件一起打包进主入口(比如 polyfill + app.js),但不推荐用于构建多个独立页面或功能模块

注意:

✅ 数组形式的 entry仍然是单入口(只有一个 chunk),只是把多个文件“一起打包”而已,不是多入口!

2. Output(输出)

output是告诉 Webpack 如何处理打包后的资源,包括打包生成的文件名、输出路径、库的导出方式等

  • filename:输出文件名(支持 [name]、[hash] 等占位符)。

  • path:输出目录(需使用 path.resolve 生成绝对路径)。

  • publicPath:资源引用路径(如 CDN 地址)。

  • library/ libraryTarget: 打包为库时使用(比如导出为 UMD、全局变量等)

  • chunkFilename: 非入口 chunk 的命名方式

常见配置:

const path = require('path');module.exports = {output: {filename: 'bundle.js',             // 输出文件名path: path.resolve(__dirname, 'dist'), // 输出目录(必须是绝对路径)}
};

多入口时,可以使用占位符 [name]

output: {filename: '[name].bundle.js', // 比如 main.bundle.js, admin.bundle.jspath: path.resolve(__dirname, 'dist'),
}

3. Loader(加载器)

Loader 是 Webpack 用来预处理模块(文件)的工具。因为 Webpack 本身只理解 JavaScript,当你要处理如 .css.png.jsx等非 JS 文件时,就需要使用对应的 loader 将它们转换成 Webpack 能够处理的模块。

本质上是一个函数,接收源文件内容作为输入,返回转换后的内容(通常是 JS 模块代码)。你可以自己编写自定义 loader。

核心特点:

  • 转换作用:将非 JS 文件转换为 JS 模块(或可被打包的资源)
  • 链式调用:多个 loader 可以组合使用,执行顺序从右到左 / 从下到上
  • 模块化:每个 loader 处理一种特定类型的文件

常见 Loader 示例:

module.exports = {module: {rules: [{test: /\.js$/,            // 匹配 .js 文件exclude: /node_modules/,use: 'babel-loader'       // 将 ES6+ 转为 ES5},{test: /\.css$/,use: ['style-loader', 'css-loader'] // 先 css-loader 解析 CSS,再 style-loader 注入样式},{test: /\.(png|jpg|gif)$/,type: 'asset/resource'    // webpack 5 内置资源模块,处理图片}]}
};

Webpack 5 开始推荐使用 Asset Modulesasset/resource, asset/inline, asset/source, asset)替代原来的 file-loaderurl-loader等。

4. Plugin(插件)

Plugin 是 Webpack 的扩展机制,用于在打包过程中执行更广泛的任务,比如优化、资源管理、环境变量注入、打包分析等。插件可以操作 Webpack 构建生命周期的各个阶段,功能比 Loader 更强大和全局化。

Loader 不同,Loader 主要用于转换某一类文件(模块)的内容,而 Plugin 则作用于整个构建流程,可以控制打包的各个阶段,甚至修改输出结果

核心特点:

  • 是一个具有 apply(compiler)方法的 JavaScript 对象
  • 可以监听 Webpack 提供的钩子(hooks),介入构建过程
  • 用于完成 Loader 无法完成的任务,如打包优化、资源管理、注入环境变量等
  • 可以操作 Webpack 的内部实例(如 compiler、compilation 对象)

常见 Plugin 示例:

const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {plugins: [new HtmlWebpackPlugin({template: './src/index.html' // 自动生成 HTML 并自动引入打包后的 JS}),new CleanWebpackPlugin() // 清理 dist 目录]
};

自定义插件示例:

class MyPlugin {apply(compiler) {compiler.hooks.done.tap('MyPlugin', () => {console.log('打包完成!');});}
}

5. Module(模块)

在 Webpack 中,一切皆模块(Everything is a module)。无论是 JS、CSS、图片、字体,还是其他资源,Webpack 都把它们看作是一个模块,并通过依赖关系进行管理。

Webpack 支持的模块类型包括:

  • ES Module (import ... from ...)
  • CommonJS (require(...))
  • AMD
  • UMD
  • 全局变量形式的脚本(通过一些 loader 也可处理)

模块特点:

  • 每个模块有自己独立的作用域
  • 模块之间通过 import/require建立依赖关系
  • Webpack 会分析这些依赖,构建出一个完整的依赖图

6. Dependency Graph(依赖图)

依赖图(Dependency Graph)是 Webpack 最核心的机制之一。它是 Webpack 根据你的 entry文件,递归分析所有模块之间的依赖关系后,构建出来的一张“模块依赖关系网”。

构建过程:

  1. entry开始,分析该模块依赖了哪些其他模块(比如通过 importrequire引入的)
  2. 再分析这些被依赖模块的依赖,递归进行
  3. 最终形成一棵 / 一张 模块依赖树 / 图

作用:

  • Webpack 根据这个依赖图,知道哪些模块需要被打包
  • 它决定了最终打包生成的代码结构与内容
  • 是 Webpack 实现模块化打包的基础

简单来说:没有依赖图,Webpack 就不知道要打包哪些代码。

二、Webpack 使用的模块规范

Webpack 本身不限定于某一种模块规范,它通过解析不同模块语法,支持以下常见模块系统:

  • ES Modules (ESM)import/export(推荐,也是现代前端的主流方式)
  • CommonJS (CJS)require/module.exports(Node.js 的模块规范)
  • AMDdefine/require(常用于浏览器端异步模块加载,如 RequireJS)
  • UMD – 通用模块定义,兼容 AMD、CommonJS 和全局变量方式
  • 全局变量(无模块化) – 比如直接通过 <script>引入的库,通过 expose-loader或插件也可处理

1.AMD

前端最早的模块化规范之一,由 RequireJS(2009 年)推广普及,解决浏览器环境中 异步加载模块 的问题。

为什么需要 AMD?

在 ES6 模块(import/export)出现前,前端代码通常通过 <script>标签直接引入 JS 文件,这种同步加载方式会导致:

  • 页面渲染被阻塞(必须等待所有脚本下载并执行完成)。
  • 模块间依赖关系复杂(需手动管理加载顺序,如 jQuery必须在 Bootstrap前加载)。

AMD 通过异步加载显式声明依赖解决了这些问题。

AMD 的核心规则

1.模块定义:使用 define函数定义模块,第一个参数是模块名(可选),第二个参数是依赖数组,第三个参数是工厂函数(返回模块导出的内容)。

// 定义一个名为 "math" 的模块,依赖 "jquery"
define("math", ["jquery"], function($) {// 模块逻辑const add = (a, b) => a + b;return { add }; // 导出对象
});

2.依赖声明:工厂函数的参数与依赖数组一一对应(如 ["jquery"]对应 $),AMD 会自动异步下载依赖的模块文件,并在所有依赖加载完成后执行工厂函数。

3.模块加载:使用 require函数加载模块(同样由 RequireJS 提供),支持异步回调。

// 加载 "math" 模块,依赖加载完成后执行回调
require(["math"], function(math) {console.log(math.add(1, 2)); // 输出 3
});

AMD 的特点

  • 纯前端规范:仅适用于浏览器环境,不涉及 Node.js。
  • 异步优先:依赖加载不阻塞页面渲染,适合浏览器端的动态模块管理。
  • RequireJS 主导:AMD 规范由 RequireJS 库实现,是早期前端模块化的主流方案(尤其在 2015 年 ES6 模块普及前)。

2.UMD(通用模块定义)

UMD 是前端为解决 多环境兼容性 而设计的模块化规范(约 2014 年),目标是让同一个模块代码能同时运行在 浏览器Node.js 或支持其他模块系统(如 AMD、CommonJS)的环境中。

为什么需要 UMD?

前端模块化规范曾长期分裂:

  • 浏览器端有 AMD(RequireJS)、CommonJS(早期 Node.js 风格,但浏览器不直接支持)。
  • Node.js 只支持 CommonJS(require/module.exports)。
  • 库开发者若想让模块同时被浏览器和 Node.js 使用,需编写多套代码。

UMD 通过环境检测动态适配解决了这一问题。

UMD 的核心逻辑

UMD 本质是一个“包装函数”,通过检测当前运行环境,选择对应的模块暴露方式。典型代码结构如下:

(function (root, factory) {// 检测是否为 AMD 环境(如 RequireJS)if (typeof define === "function" && define.amd) {define(["dependency"], factory); // 按 AMD 规范定义模块} // 检测是否为 Node.js/CommonJS 环境else if (typeof module === "object" && module.exports) {module.exports = factory(require("dependency")); // 按 CommonJS 规范导出} // 浏览器全局环境(无模块系统)else {root.myModule = factory(root.dependency); // 直接挂载到全局对象(如 window)}
})(this, function (dependency) {// 模块核心逻辑const func = () => { /* ... */ };return { func }; // 导出内容
});

UMD 的特点

  • 多环境兼容:同一份代码可在浏览器(AMD/全局变量)、Node.js(CommonJS)中运行。
  • 库开发的常用方案:早期前端库(如部分 jQuery 插件、工具库)常用 UMD 打包,避免用户手动处理模块系统差异。
  • 灵活性:通过简单的代码包装即可实现跨环境,无需依赖额外工具(现代打包工具如 Webpack/Rollup 已内置类似能力)。

三、Webpack 的构建流程(内部工作原理 / 打包过程)

Webpack 的构建流程是一个非常精细的过程,主要可以分为如下几个阶段 👇:

注意:Webpack 构建流程是基于 Tapable 钩子机制 控制的,它通过一系列生命周期钩子来驱动整个打包流程。


  1. 初始化:读取配置,创建 Compiler
  2. 编译:从 entry 开始递归分析依赖,构建 Dependency Graph,用 Loader 处理模块
  3. 优化:执行 Tree Shaking、代码分割、作用域提升等优化
  4. 生成:将模块打包为最终文件,计算 Hash,写入磁盘
  5. 完成:输出打包结果,触发完成钩子
1.Tapable
1.1 定义

Tapable 是一个轻量级的库,用于实现插件系统与事件驱动的架构,它通过“钩子(Hooks)”机制,允许开发者在代码执行过程中的特定节点“注入”自定义逻辑。

Webpack 内部大量使用了 Tapable 来管理其构建流程中的各个阶段,使得 Webpack 的插件机制变得非常灵活和强大。

🎯 更通俗的理解:

想象成一个事件系统 / 发布-订阅机制,它定义了很多不同类型的“钩子”,你可以在这些钩子上“注册”(tap)你的逻辑(也就是插件要做的事),当 Webpack 运行到某个阶段时,就会“触发”(call)这些钩子,从而执行你注册的逻辑。

1.2 为什么 Webpack 需要 Tapable?

Webpack 是一个高度可扩展的构建工具,它的核心流程(比如解析模块、编译、优化、生成资源等)非常复杂,但它并不直接实现所有功能,而是通过 插件机制 把这些流程的各个阶段暴露出来,让开发者或插件作者可以在特定节点插入自己的逻辑。

为了实现这种灵活的插件系统与生命周期控制,Webpack 使用了 Tapable 这个库来提供:

  • 各种类型的“钩子”(Hooks),用于定义事件点
  • 提供 “tap”(注册)、“call”(触发)等 API,让插件可以介入构建过程
  • 支持同步 / 异步的插件逻辑执行
1.3 Tapable 提供的钩子

Tapable 提供了多种不同类型的 Hook,用于支持同步、异步、串行、并行等不同场景的插件介入方式。

下面是常见的 Hook 类型(都是 Tapable 提供的类):

钩子类型触发方式说明适用场景
SyncHook同步串行按注册顺序同步依次执行适用于同步任务,比如初始化阶段
SyncBailHook同步串行,可中断如果某个插件返回非 undefined值,则停止后续执行比如解析模块时,一旦命中缓存就不再继续
SyncWaterfallHook同步串行,可传递结果每个插件返回的值,会作为参数传给下一个插件比如某些需要累积结果的场景
SyncLoopHook同步循环如果插件返回 true,则重复执行该插件比如某些需要不断重试的操作
AsyncParallelHook异步并行多个插件同时异步执行适用于可并行处理的任务
AsyncParallelBailHook异步并行,可中断异步并行,某个插件返回非 undefined 可中断
AsyncSeriesHook异步串行多个异步插件按顺序依次执行最常见,比如编译过程、生命周期钩子
AsyncSeriesBailHook异步串行,可中断异步串行,某个插件返回非 undefined 则停止后续
AsyncSeriesWaterfallHook异步串行,可传递结果异步串行,前一个插件的返回值传给下一个比如需要逐步处理并传递数据

🧠 Webpack 内部大量使用这些 Hook 类,比如 compiler.hooks.run是一个 AsyncSeriesHook,它控制 Webpack 开始运行时的生命周期。

1.4 Tapable 是如何控制 Webpack 构建流程的?

核心思想:

Webpack 将构建流程中的各个关键节点抽象为一个个 Tapable 的 Hook(钩子),插件作者可以通过 tap 方法在这些钩子上注册自己的逻辑,当 Webpack 执行到对应阶段时,会通过 call 方法触发这些钩子,从而执行插件逻辑。


1.Webpack 核心对象:Compiler 和 Compilation

  • Compiler:代表整个 Webpack 构建过程,是全局唯一的,负责管理整个构建流程
  • Compilation:代表一次具体的编译过程,管理模块的编译、依赖图构建、优化等

这两个对象上都挂载了大量的 Tapable Hooks,插件可以通过它们介入到构建的各个阶段。

2.插件如何介入构建流程?

一个 Webpack 插件本质上是一个具有 apply(compiler)方法的 JavaScript 对象。在 apply方法中,插件通过 compiler.hooks.xxx.tap(...)注册到 Webpack 的某个生命周期钩子上。

四、Webpack 的使用场景

场景说明
单页应用 (SPA)React / Vue 等框架项目,模块化开发,热更新,代码分割
多页应用 (MPA)多个 HTML 页面,每个页面独立入口,配合 HtmlWebpackPlugin
组件库 / 工具库开发打包为 UMD / ESM / CommonJS,支持多环境、Tree Shaking
静态资源处理图片、字体、样式等资源的模块化打包与优化
工程化与开发体验HMR、devServer、source map、环境变量等
微前端 / 模块联邦Webpack 5 模块共享方案,适合大型拆分项目
http://www.xdnf.cn/news/1301293.html

相关文章:

  • 思考:高速场景的行星轮混动效率如何理解
  • 解决Electron透明窗口点击不影响其他应用
  • 启动electron桌面项目控制台输出中文时乱码解决
  • 下载及交叉编译zlib库,记录
  • 解决ECharts图表上显示的最小刻度不是设置的min值的问题
  • 从源码到可执行文件:hello.c 的二进制之旅
  • 【Golang】:数据类型
  • Wi-Fi 与蜂窝网络(手机网络)的核心区别,以及 Wi-Fi 技术未来的发展方向
  • Redisson分布式锁实战指南:原理、用法与项目案例
  • GPT 解码策略全解析:从 Beam Search 到 Top-p 采样
  • 流处理、实时分析与RAG驱动的Python ETL框架:构建智能数据管道(上)
  • CPU、内存、存储:生信分析任务的服务器配置精要
  • 第20章 LINQ 笔记
  • 8.15网络编程——UDP和TCP并发服务器
  • 【数据分享】上市公司创新韧性数据(2007-2023)
  • 数据驱动测试提升自动化效率
  • 终极手撸cpu系列-详解底层原理-CPU硬核解剖:从0和1到 看透CPU逻辑设计内部原理,弄清楚现代多线程cpu工作原理
  • Microsoft Visual Studio常用快捷键和Windows系统常用快捷键的整理
  • Linux-地址空间
  • 开发避坑指南(27):Vue3中高效安全修改列表元素属性的方法
  • 【学习笔记】NTP服务客户端配置
  • Go语言中安全停止Goroutine的三种方法及设计哲学
  • 前瞻性技术驱动,枫清科技助力制造企业借助大模型完成生产力转化
  • zabbix部署问题后常见问题
  • 新手入门Makefile:FPGA项目实战教程(二)
  • 【CV 目标检测】②R-CNN模型
  • 【Redis】分布式系统的演化过程
  • MyBatis的基本用法和配置方式
  • Highcharts Dashboards | 打造企业级数据仪表板:从图表到数据驾驶舱
  • 全球电商业财一体化:让出海品牌实现“看得见的增长“