🧩 一、Webpack 是什么?解决了什么问题?
想象你在开一家“奶茶店 🍹”。 原料有:茶叶、牛奶、糖、水果、冰块…… 这些都得先准备好、分类好、最后“打包”成一杯奶茶。
前端开发也一样:
- 写前端,就像把所有原料散乱摆在桌上(多个
.js、.css、.png各自独立)。 - 浏览器要一一去拿,非常慢。
- 而 Webpack 就像奶茶机,能把这些资源统统打包成一个“Bundle 奶茶杯”。
🧠 二、模块化演进过程
早期 JS 写法是这样的:
<script src="module-a.js"></script>
<script src="module-b.js"></script>
问题:
- 每个模块独立加载,全局变量污染;
- 顺序依赖,
b可能要等a; - 模块太多会让浏览器加载很慢。
于是开发者发明了各种“模块化方案”👇
// 最原始方式
window.moduleA = {
method1: function () {
console.log('moduleA#method1')
}
}
这种方式等于:
所有员工(函数)都在一个“开放办公室”,互相干扰,容易出错。
后来改进为:
// module-a.js
(function ($) {
var name = 'module-a'
function method1() {
console.log(name + ' #method1')
$('body').animate({ margin: '200px' })
}
window.moduleA = { method1: method1 }
})(jQuery)
这相当于:
每个员工(模块)搬进了独立的办公室(闭包),只暴露必要的窗口(window.moduleA)。
但依然有问题:
- 文件太多;
- 加载顺序难控制;
- 代码容易冗余。
于是,Webpack 出场!
🧱 三、Webpack 是如何解决这些问题的
Webpack 是一个“智能打包机”:
- 它会扫描整个项目的模块依赖;
- 把所有
.js、.css、.png、.scss文件都看成“模块”; - 最后统一打包生成
bundle.js、bundle.css等文件。
就像👇这张图:
.js → 依赖图
.scss → 打包
.hbs → 模板
.png → 资源
→ 都汇入中心立方体(Webpack) → 输出 .js、.css、.png、.jpg
一句话记住:
Webpack 帮你“把散落的原料打成一杯可直接喝的奶茶”。
⚙️ 四、Webpack 的三个重要能力
1️⃣ 编译代码能力
把 ES6 转成 ES5,让旧浏览器也能喝下奶茶。
- 开发阶段写的现代语法 ES6
- 编译后变成兼容版本 ES5
举例:
// 开发写法
const add = (a, b) => a + b
// 编译后
var add = function(a, b) { return a + b; }
➡️ 类似“把日语菜单翻译成中文”,让所有顾客都能看懂。
2️⃣ 模块整合能力
把上百个 .js 文件整合成一个打包文件,减少浏览器请求次数。
→ 以前浏览器要发 100 个请求(每次加载一个模块) → 现在只需要 1 个 bundle.js
➡️ 就像“把100个小料盒统一装进一个礼盒”,拿一次就行。
3️⃣ 万物皆可模块
不止 JS,连 .css、.png、.ts、.scss、.vue 都能模块化处理。
例如:
import './style.css'
import img from './logo.png'
➡️ Webpack 就像万能榨汁机,扔进去都能变成果汁。
🔥 五、Webpack 热更新原理
问题: 修改一点小代码就得“重启整个应用”?太慢!
于是出现了 HMR(Hot Module Replacement)模块热替换。
💡 是什么?
HMR 可以让你:
在应用运行时,实时替换修改的模块,而不用整页刷新。
比如:
- 你在写 Vue 页面,改了一个按钮颜色;
- Webpack 热更新只刷新那一块按钮样式;
- 页面状态、输入内容不会丢。
🧩 实现原理(简化理解)
Webpack Dev Server:
- 启动一个 本地服务器;
- 页面运行时通过 WebSocket 与服务器保持通信;
- 一旦文件变化,服务器告诉浏览器;
- 浏览器只替换那部分模块,不刷新整页。
➡️ 就像“做奶茶时换了水果”,机器只替换水果,不重新冲整杯。
🧭 六、总结口诀
🌐 模块太乱 → Webpack 出现 🧱 打包整合 → 减少请求 ⚙️ 编译转换 → 浏览器兼容 🔥 热模块换 → 无需刷新
一句话记住: Webpack 是前端界的“中央厨房” 负责把所有食材(代码、图片、样式)处理好、打包好、热更新快。
一、Webpack 热更新(HMR)原理全流程
📖 关键词:HMR = Hot Module Replacement(模块热替换)
1️⃣ 是什么?
Webpack 热更新(HMR)可以在不刷新整个页面的情况下,实时更新你修改的部分模块。 也就是说:
- 你改了样式文件(
style.css) → 页面立刻生效; - 页面上的表单输入、滚动位置、状态数据都不会丢失。
举个生活例子 🧋:
你在经营一家奶茶店(前端项目)。 顾客(浏览器)正在喝奶茶(运行项目), 这时候你发现水果味太淡(代码有 bug), 你只换掉水果,不用倒掉整杯奶茶(只更新模块,不刷新全页), 这就是 HMR 的作用。
2️⃣ 如何启用 HMR
你只要在 Webpack 配置中打开这个开关就行:
const webpack = require('webpack')
module.exports = {
// ...
devServer: {
hot: true, // ✅ 启用 HMR 热更新
hotOnly: true // 即使热更新失败也不强制刷新
}
}
这样当你保存文件时,Webpack 会自动编译修改的模块并替换到页面中。
如果你还想精准控制某个模块的更新逻辑,可以在代码里加上:
if (module.hot) {
module.hot.accept('./util.js', () => {
console.log('util.js 更新了!')
})
}
📘意思是:
“如果这个模块支持热更新,当我改动
util.js时,不刷新页面,只替换这个模块。”
3️⃣ 内部原理(通俗解释)
看这张图👇(图7页)
Webpack 的热更新其实是一个“后台通信+文件替换”机制。
🔧 关键角色:
| 模块 | 作用 |
|---|---|
| Webpack Compiler | 负责编译源代码,生成 bundle.js |
| HMR Server | 管理热更新相关文件 |
| Bundle Server | 提供静态资源访问路径 |
| HMR Runtime | 注入浏览器中,接收更新通知 |
| socket server | 建立浏览器与服务器的长连接(WebSocket) |
🧩 运行分两个阶段:
🔹 阶段1:启动阶段
webpack compile把源码编译为bundle.js- 同时生成 HMR runtime(热更新运行时)
- 输出给
bundle server静态服务器 - 浏览器加载页面,HMR runtime 也被注入到浏览器中
🔹 阶段2:更新阶段
-
当文件变化(比如
util.js) → Webpack 重新编译生成新的模块; -
生成两个文件:
manifest.json(说明更新了哪些模块、hash值)update.js(新模块代码)
-
socket server通知浏览器有更新; -
浏览器拿到更新内容后,通过
HMR runtime替换旧模块。
📦 类比记忆:
HMR 就像外卖系统:
- HMR Server 是外卖后台;
- socket 是骑手;
- HMR runtime 是顾客的手机;
- 当你换了一个食材(文件改动)时,后台(Webpack)重新打包一份新的配料;
- 骑手(socket)马上送到顾客(浏览器),手机上自动替换掉旧的食材(模块)。
4️⃣ 具体运行过程
当浏览器和 socket server 建立连接后:
- 浏览器会收到包含 hash 值的更新信息。
- 浏览器根据
manifest.json请求变化的模块。 - 新模块加载完成后执行替换(render 流程)。
- 整个页面无刷新更新成功 🎉
5️⃣ 小结
Webpack 热更新完整流程总结:
-
webpack-dev-server启动两个服务器:- 一个静态资源服务器(
express) - 一个
socket server(用 WebSocket 通信)
- 一个静态资源服务器(
-
文件改动后 Webpack 重新编译模块
-
生成两个文件:
.json(manifest 文件,记录更新信息).js(新模块代码)
-
通过 socket 发送通知到浏览器
-
浏览器用 HMR runtime 拉取新文件 → 替换旧模块
📘 口诀记忆:
🧠 改文件 → 📦 重新编译 → 🔗 通知浏览器 → 🔁 替换模块
或者更形象一点👇
“换水果不用倒奶茶,热更新让页面更香。”
⚙️ 二、Webpack 构建流程
Webpack 的构建其实就像一个流水线工厂。 它把你写的源文件(原材料)一步步加工成浏览器能直接喝的“成品奶茶(bundle.js)”。
🌈 一共分三大阶段:
| 阶段 | 作用 | 类比 |
|---|---|---|
| 初始化阶段 | 读取配置、加载插件 | “准备食材 + 启动机器” |
| 编译构建阶段 | 从入口分析依赖,翻译文件 | “自动化搅拌加工” |
| 输出阶段 | 把结果写入文件系统 | “包装打包出货” |
🔹 1. 初始化阶段
Webpack 会做两件事:
// 从配置文件和命令行读取参数
// 得出最终的配置结果
webpack.config.js
- 读取配置文件(默认:
webpack.config.js) - 解析命令行参数(如
--mode development) - 合并为最终配置
- 加载需要的插件(比如 HtmlWebpackPlugin)
🧠 类比:
店长查看菜单 + 打开机器 + 加载配方。
🔹 2. 编译构建阶段
从入口文件(Entry)开始,分析依赖关系,递归构建所有模块。
Webpack 会:
- 读取入口文件;
- 对每个模块调用相应的 Loader 进行翻译;
- 生成模块依赖图;
- 最终形成 Chunk(代码块)。
// webpack 会从 entry 出发
entry: './src/index.js'
- 如果遇到
import './style.css'→ 调用css-loader处理 - 如果遇到图片 → 调用
file-loader打包
🧠 类比:
食材(代码)被自动分拣、切配、加料、搅拌。
🔹 3. 输出阶段
Webpack 把最终的模块组合打包为 Chunk,再写入输出目录。
output: {
filename: 'bundle.js',
path: __dirname + '/dist'
}
🧠 类比:
最终装杯、封膜、贴标签(输出文件)。
📊 流程图理解
初始化 → 编译 → 输出
↑ ↓
文件变化 → 重新编译(热更新)
Webpack 一直循环执行这三步,配合 HMR 就能做到实时更新。
✅ 总结口诀
| 阶段 | 核心动作 | 关键词 |
|---|---|---|
| 初始化 | 读配置,加载插件 | webpack.config.js |
| 编译构建 | 从入口出发,分析依赖 | loader + module |
| 输出 | 合并生成 bundle | chunk + output |
🌈 一句话记忆:
“Webpack 三步曲:读配置 → 编代码 → 出文件。”
🏭 一、Webpack 构建像一条“自动化工厂流水线”
想象 Webpack 就是一家「智能饮料工厂」🍹 你把各种原材料(JS / CSS / 图片)送进去,它会自动:
- 读取菜单(配置文件);
- 按配方混合(编译);
- 封装包装(打包输出);
- 把成品送上货架(dist 文件夹)。
🧱 二、Webpack.config.js 的作用
Webpack 先读取你的配置文件:
var path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js', // 项目入口(进货口)
output: { // 出货口
path: path.resolve(__dirname, 'build'),
filename: '[name].js'
},
module: { // 加工方式(机器设备)
loaders: [
{
test: /.js$/,
loader: 'babel', // 使用 Babel 转译 ES6+
query: { presets: ['es2015', 'react'] }
}
]
},
plugins: [ // 工厂中的“插件助手”
new webpack.HotModuleReplacementPlugin()
]
};
🧠 生活类比:
entry:原料入口 → 告诉工厂原料从哪儿来;output:出口仓库 → 告诉工厂产品存哪;loader:加工机器 → 把 ES6 / SCSS 等转成浏览器能喝的“果汁”;plugin:自动包装、贴标签等智能设备。
🧩 三、Compiler 对象
Webpack 的“厂长”就是 Compiler 对象,它管理整个流程。
class Compiler extends Tapable {
constructor(context) {
super();
this.hooks = {
beforeCompile: new AsyncSeriesHook(["params"]),
compile: new SyncHook(["params"]),
afterCompile: new AsyncSeriesHook(["compilation"]),
make: new AsyncParallelHook(["compilation"]),
entryOption: new SyncBailHook(["context", "entry"])
};
}
}
📘 解释:
- Webpack 内部全靠这些 “钩子(hook)” 来驱动;
- 每个阶段(compile / make / emit)就像生产线的一个“机器”;
- Loader、Plugin 都能“挂钩子”,在对应环节执行自己的任务。
🧠 类比:
这就像一条流水线,每个机器(hook)在不同阶段接手任务。
⚙️ 四、Webpack 的 5 大核心阶段
Webpack 启动后,会按顺序执行这五步👇:
compile → make → build-module → seal → emit
| 阶段 | 作用 | 类比 |
|---|---|---|
| compile | 开始编译,初始化构建对象 | 开机准备 |
| make | 从入口文件出发,收集依赖 | 收集原料 |
| build-module | 调用 loader 翻译模块 | 加工处理 |
| seal | 封装模块,生成 chunk | 装瓶封口 |
| emit | 输出打包文件到磁盘 | 成品出库 |
🧱 五、make 阶段详解
Webpack 开始读取入口文件,执行 _addModuleChain():
_addModuleChain(context, dependency, onModule, callback) {
const Dep = dependency.constructor;
const moduleFactory = this.dependencyFactories.get(Dep);
moduleFactory.create({ dependencies: [dependency] }, (err, module) => {
const afterBuild = () => {
this.processModuleDependencies(module, err => {
callback(null, module);
});
};
this.buildModule(module, false, null, null, err => {
afterBuild();
});
});
}
📘 解释步骤:
- 入口文件(
entry)传进来; - 找到合适的模块工厂(
NormalModuleFactory); - 创建一个空的
module; - 调用
buildModule()→ 真正开始解析和编译模块。
🧠 类比:
工厂接到“订单”后,建立一个空的生产框架(module),然后开始加工每个零件。
🧩 六、build module 阶段
Webpack 使用 Loader 把各种文件转换成标准 JS 模块:
- JS → Babel 转译成 ES5;
- SCSS → 编译成 CSS;
- 图片 → 转成 base64 或文件路径。
// 举个例子
{
test: /.js$/,
loader: 'babel-loader',
query: { presets: ['es2015', 'react'] }
}
转换完后,用 acorn 解析成 AST(抽象语法树) ,方便分析 import / require 的依赖关系。
🧠 类比:
这一步就像“配料混合”阶段:所有原材料都被搅拌、加工,形成标准饮料浆。
🧾 七、seal 阶段(封装输出)
seal() 方法会生成最终的 chunk:
- 每个 chunk 就是一组相关模块;
- 这些 chunk 会被进一步优化(如代码压缩);
- 然后准备输出成实际文件。
🧠 类比:
把饮料倒进瓶子里、贴标签、盖上盖子。
📦 八、emit 阶段(输出文件)
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js'
}
Webpack 会根据这个配置,把结果写进磁盘。
🧠 类比:
所有瓶装饮料都装进箱子里,放进“build 仓库”。
🧩 九、整体流程图
entry-option → run → make → build-module → program → seal → emit
🧠 流程口语化总结:
读取入口配置 → 启动编译 → 分析依赖 → 构建模块 → 生成 chunk → 打包输出。
✅ 十、一句话总结记忆口诀
Webpack 像一座自动工厂:
entry是原料入口;loader是加工机器;plugin是智能助手;chunk是装瓶环节;emit是出货打包。
一句话背诵:
「Webpack 五步走:Compile 启动 → Make 收料 → Build 加工 → Seal 封装 → Emit 出货」。
🚦 一、Webpack Proxy:解决跨域问题(第16~17页)
🔍 1️⃣ 是什么
webpack proxy —— 就是 Webpack 内置的“代理服务器” 。
你可以把它理解成一个「中间人 🧑💼」, 帮你把前端发的请求“中转”到后台服务器,从而绕过浏览器的跨域限制。
// webpack.config.js
const path = require('path');
module.exports = {
devServer: {
contentBase: path.join(__dirname, 'dist'),
port: 9000,
proxy: {
'/api': {
target: 'https://api.github.com' // 目标服务器
}
}
}
}
🧠 类比说明: 你在公司(前端)要和总部(后端)沟通, 但安全部门(浏览器同源策略)不让你直接联系总部。 于是你找了前台(proxy server)帮你“转发消息”, 这样总部以为是前台发的,就不会被安全拦截。
⚙️ 2️⃣ 工作原理(第16页)
Proxy 内部其实用了一个中间件包 👉 http-proxy-middleware。
原理:
- 浏览器请求本地
localhost:3000/api/user; - Webpack Dev Server 拦截这个请求;
- Proxy 把它转发到远程
https://api.github.com/api/user; - 然后再把响应结果返回给浏览器。
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
app.use('/api', proxy({ target: 'http://www.example.org', changeOrigin: true }));
app.listen(3000);
访问:
http://localhost:3000/api/foo/bar
⇒ 自动代理到 http://www.example.org/api/foo/bar
🌐 3️⃣ 为什么能解决跨域(第17页)
跨域问题本质是浏览器的安全限制。 但代理服务器和目标服务器之间没有这个限制!
代理请求的流程:
浏览器 → 本地 webpack server (同源) → proxy → 目标服务器 (跨域)
浏览器只看到 “本地 server”,所以不会触发跨域警告。
📘 类比:
你本人(浏览器)不能直接给外国总部发快递(跨域受限), 但你可以交给“公司前台”(proxy)转发过去。 对总部来说,是公司发的,不是你个人,所以没问题。
💬 4️⃣ Proxy 参数小抄(背诵重点)
| 参数 | 作用 | 举例 |
|---|---|---|
target | 代理目标服务器地址 | 'https://api.github.com' |
pathRewrite | 重写路径 | '/api' → '' |
secure | 是否校验证书(HTTPS) | false |
changeOrigin | 是否修改请求头里的 Host | true |
🧱 二、Webpack Loader:文件处理器(第18~20页)
🧩 1️⃣ 是什么(第18页)
loader 就是 Webpack 的“文件翻译官” 。 Webpack 默认只认识 .js 和 .json 文件, 遇到 .css、.png、.vue、.ts 等其他文件就懵了。
于是 loader 登场了,它能把各种文件“翻译”成 JS 能理解的模块。
📘 类比: Webpack 是个外国人(只懂 JS), Loader 就是翻译官, 帮他把中文、图片、样式等“翻译成 JS”。
举例:
import './style.css' // 需要 css-loader + style-loader
import logo from './logo.png' // 需要 file-loader 或 asset 模块
Webpack 在导入时,会依次调用配置好的 loader 进行处理。
🧭 2️⃣ Loader 执行顺序(第19页)
Webpack 加载模块时执行顺序如下:
entry → loaders → output
- entry:入口文件
- loaders:对各种资源进行加工
- output:输出最终打包结果
当 Webpack 碰到不认识的文件类型时,会根据配置文件去找对应 loader。
⚙️ 3️⃣ Loader 的配置方式(第19页)
三种方式:
| 方式 | 示例 | 特点 |
|---|---|---|
| 配置文件方式(推荐) | 写在 webpack.config.js 中 | 最常用 |
| 内联方式 | 在 import 里写 import 'style-loader!css-loader!./style.css' | 一次性使用 |
| CLI 命令方式 | webpack --module-bind 'css=style-loader!css-loader' | 临时 |
🧾 4️⃣ 常见配置(第20页)
module.exports = {
module: {
rules: [
{
test: /.css$/, // 匹配 .css 文件
use: [
{ loader: 'style-loader' }, // 把样式插入到页面中
{ loader: 'css-loader', options: { modules: true } }, // 解析 @import、url()
{ loader: 'sass-loader' } // 把 scss 转成 css
]
}
]
}
};
执行顺序是从 下往上:
sass-loader → css-loader → style-loader
🧠 类比:
就像做一杯珍珠奶茶:
sass-loader:先搅拌原料;css-loader:把材料整理成可饮用的;style-loader:把成品倒进杯子(注入 HTML)。
🌟 5️⃣ Loader 的特性(第20页)
| 特性 | 含义 |
|---|---|
| 可同步也可异步 | 支持异步加载文件(例如图片) |
| 运行在 Node.js 环境 | 所以 loader 实际上是 JS 函数 |
| 支持链式调用 | 一个文件可被多个 loader 连续处理 |
| 可单独开发/发布 | 可自定义 loader 并发布到 npm |
| 可配合插件 | 插件能扩展 loader 功能(压缩、打包等) |
⚡ 举个生活例子帮你记:
想象 Webpack 是厨房,Loader 是厨师。 每个文件是“食材”,要经过不同厨师的加工:
sass-loader:原料切配(把 SCSS 变成 CSS)css-loader:整合调料(解析 import / url)style-loader:上菜(把 CSS 注入页面)最后所有菜(资源文件)都被端上桌(output)🎉。
✅ 三、快速复习口诀
| 功能 | 一句话理解 |
|---|---|
| Proxy | 前端“前台小姐姐”,帮你把请求偷偷转到后端,绕过跨域 |
| Loader | 前端“翻译官/厨师”,把各种格式的文件翻译成 JS 能读的模块 |
📘 一句口诀背完这节:
Proxy 解跨域,Loader 译万物; 厨房配料齐,前后端都通路。 🍜
🍱 一、先搞懂 Loader 的本质(第21页)
Loader 是什么?
Loader 就是 Webpack 的“翻译官”,负责把不同类型的文件翻译成 JS 能理解的模块。
Webpack 默认只认识 .js 和 .json 文件, 当我们导入 .css、.png、.less 等文件时,就得用 loader 来翻译。
📦 类比记忆:
Webpack 是厨房,Loader 是厨师。 每个 loader 都负责“处理”一种食材:
css-loader:切菜(分析 CSS 文件)style-loader:上菜(把样式放进页面)file-loader:打包食材文件url-loader:把小图片直接“压成汁”混在代码里
🧩 二、常见 Loader 一览(第21页)
| Loader | 功能 | 类比 |
|---|---|---|
style-loader | 把样式注入到 HTML 页面中(用 <style> 标签) | 把菜端上桌 |
css-loader | 解析 @import 和 url(),让 JS 能识别 CSS 文件 | 切菜 |
less-loader / sass-loader | 把 LESS / SASS 编译成 CSS | 把半成品加工成菜 |
postcss-loader | 给 CSS 自动加浏览器前缀 | 撒调料 |
file-loader | 把文件输出到目标目录并返回路径 | 打包外卖 |
url-loader | 小图片转成 base64 编码,大图仍打包文件 | 小图混在代码里,大图分开送 |
raw-loader | 把文本文件直接以字符串形式导入 | 直接读原材料 |
🧱 三、CSS 相关 Loader(第22页)
3.1 css-loader
安装:
npm install --save-dev css-loader
配置:
rules: [
{
test: /.css$/,
use: {
loader: "css-loader",
options: {
url: true, // 处理 url() 路径
import: true, // 处理 @import
sourceMap: false // 是否生成 SourceMap
}
}
}
]
📘 作用:
css-loader只解析 CSS,不会让样式在页面生效。- 所以页面不会“变漂亮”,只是“读懂了样式”。
🧠 类比:
厨师只是切好了菜,还没端上桌。
3.2 style-loader
安装:
npm install --save-dev style-loader
配置:
rules: [
{
test: /.css$/,
use: ["style-loader", "css-loader"]
}
]
📘 执行顺序是从右到左: css-loader 先解析,再交给 style-loader 注入页面。
🧠 类比:
css-loader做好菜,style-loader把菜端上桌。
🔍 小案例:
假设有文件 style.css:
body {
background: pink;
}
在 JS 中导入:
import './style.css'
➡️ Webpack 流程:
css-loader→ 读懂 CSS 内容;style-loader→ 用<style>标签插入到<head>;- 页面变粉 💗。
🎨 四、LESS / SASS 等预处理器(第23页)
安装:
npm install less-loader -D
配置:
rules: [
{
test: /.css$/,
use: ["style-loader", "css-loader", "less-loader"]
}
]
📘 顺序:
less-loader把 LESS 转成 CSS;css-loader读懂 CSS;style-loader注入页面。
🧠 类比:
LESS 是“原料粉”,less-loader 就是厨师把粉调成汁。
📜 五、raw-loader(第23页)
raw-loader 让你直接 import 文本文件内容(比如 .txt、.md)。
安装:
npm install --save-dev raw-loader
配置:
module.exports = {
module: {
rules: [
{
test: /.(txt|md)$/,
use: 'raw-loader'
}
]
}
}
📘 举例:
import text from './about.txt'
console.log(text)
➡️ 输出:
这是 about.txt 文件的内容
🧠 类比:
raw-loader就像直接把菜谱文字端上桌,不加工,原汁原味。
🖼 六、file-loader(第24页)
file-loader 处理文件(图片、字体等), 会复制文件到输出目录,并返回它的路径。
安装:
npm install --save-dev file-loader
配置:
rules: [
{
test: /.(png|jpe?g|gif)$/,
use: {
loader: "file-loader",
options: {
name: "[name]_[hash].[ext]", // 文件命名
outputPath: "./images", // 输出路径
publicPath: "./images" // 引用路径
}
}
}
]
📘 举例:
import logo from './logo.png'
console.log(logo)
➡️ Webpack 会:
- 把图片复制到
dist/images/logo_xxxxx.png logo变量中存的是图片 URL。
🧠 类比:
file-loader = 快递员 📦 它负责把图片打包搬到目标仓库,并告诉你“地址”。
🧮 七、url-loader(第25页)
它可以做 file-loader 的所有事,还能: 👉 把小图片直接变成 base64 字符串,嵌入 JS 文件中。
安装:
npm install --save-dev url-loader
配置:
rules: [
{
test: /.(png|jpe?g|gif)$/,
use: {
loader: "url-loader",
options: {
name: "[name]_[hash].[ext]",
outputPath: "./images",
publicPath: "./images",
limit: 100 // 小于 100 字节的图片转成 base64
}
}
}
]
📘 举例:
- 图片 < 100B → 转成 base64 直接塞进 JS;
- 图片 ≥ 100B → 调用 file-loader 走正常打包流程。
🧠 类比:
小图片像“一口就吃完的糖果”,直接塞进嘴(代码); 大图片像“大蛋糕”,要单独打包送过去。
✅ 八、终极记忆口诀
| Loader | 功能 | 生活类比 |
|---|---|---|
| css-loader | 解析 CSS 文件 | 切菜 |
| style-loader | 把样式挂进页面 | 上菜 |
| less/sass-loader | 把预处理器转成 CSS | 调料机 |
| raw-loader | 导入纯文本 | 原汁呈上 |
| file-loader | 把文件复制到打包目录 | 快递员 |
| url-loader | 小图转 base64 | 糖果直接塞进嘴 |
一句话背完:
💡“Loader 六兄弟”:切菜(css) → 上菜(style) → 调料(less/sass) → 文本(raw) → 快递(file) → 糖果(url)
🧠 一、Plugin 是什么(第26页)
在 Webpack 里,Plugin 是扩展 Webpack 能力的工具。
📘 Loader 是“文件加工机”, 而 Plugin 就像工厂的“智能系统” , 它能在整个生产流程的不同阶段插入额外的功能。
比如:
- 打包前清理旧文件;
- 打包后自动生成 HTML;
- 打包时压缩代码;
- 监听状态、输出日志等。
📊 官方结构图解释:
entry → loaders → output
↑
plugins
- Loader 专注于「单个文件的转换」;
- Plugin 管理「整个构建过程的生命周期」。
🧠 生活类比:
想象 Webpack 是个“饮料工厂”:
Loader 是负责“榨汁、调味、灌装”的机器;
Plugin 是“生产主管”👨🏭, 他能:
- 开工前打扫厂房(clean);
- 每次出货自动贴标签(html-webpack-plugin);
- 实时监控温度(ProgressPlugin);
- 产品出库后打包压缩(CompressionPlugin)。
⚙️ 二、Plugin 的配置方式(第26-27页)
配置文件里通过 plugins 数组注入插件:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.ProgressPlugin(), // 显示打包进度
new HtmlWebpackPlugin({ // 自动生成 HTML
template: './src/index.html'
})
]
};
- 每个插件都要用
new关键字; - 插件一般通过
options传入配置参数。
💡 三、Plugin 的原理与特性(第27页)
插件本质上就是一个 带有 apply 方法的 JS 类, Webpack 启动时会自动调用它的 apply(compiler) 方法。
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
compiler.hooks.run.tap(pluginName, compilation => {
console.log('✅ Webpack 构建开始了!');
});
}
}
module.exports = ConsoleLogOnBuildWebpackPlugin;
📘 解释:
compiler:Webpack 编译器实例(整个构建过程的“大脑”);hooks.run.tap():监听 Webpack 的生命周期钩子;- 当编译开始时执行
console.log()。
🧠 类比:
Webpack 像一台“智能生产线”, Plugin 就是“装在每个阶段的传感器”:
- 开始运行(run) → 打印“机器启动”;
- 打包完成(done) → 通知“装箱完毕”。
🔁 Webpack 编译生命周期钩子:
| 钩子 | 时机 | 类比 |
|---|---|---|
| entry-option | 读取入口配置 | 打开菜单 |
| run | 开始编译 | 机器开动 |
| compile | 创建编译对象 | 材料入库 |
| make | 解析依赖并构建模块 | 搅拌加工 |
| after-compile | 构建完成 | 等待出货 |
| emit | 文件写入磁盘前 | 装箱中 |
| done | 打包完成 | 出厂 |
| failed | 打包出错 | 报警灯亮 |
🧩 四、常见的 Plugin(第28-29页)
下面是常用插件表格(我帮你提炼记忆关键词 👇):
| 插件名 | 功能 | 记忆口诀 |
|---|---|---|
HtmlWebpackPlugin | 自动生成 HTML 并插入打包的 JS | 📄 自动上架 |
CleanWebpackPlugin | 打包前清空旧文件 | 🧹 清仓再出货 |
DefinePlugin | 定义全局变量 | 🧩 注入环境变量 |
HotModuleReplacementPlugin | 热更新(不刷新页面) | 🔥 实时调味 |
CompressionWebpackPlugin | 压缩资源 | 📦 瘦身出货 |
CopyWebpackPlugin | 复制静态文件 | 📁 搬运工 |
BannerPlugin | 给 bundle 添加版权说明 | 🏷️ 打标签 |
AggressiveSplittingPlugin | 把大文件分割成小 chunk | 🍰 切块装盘 |
EnvironmentPlugin | 读取环境变量 | 🌍 环境开关 |
MiniCssExtractPlugin | 把 CSS 单独提取 | 💅 分离样式文件 |
🧠 五、常用插件详解(第29-30页)
✅ 1. HtmlWebpackPlugin
自动生成一个 HTML 文件,并把打包后的 JS 自动插入其中。
安装:
npm install --save-dev html-webpack-plugin
使用:
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
title: 'My App',
filename: 'app.html',
template: './src/html/index.html'
})
]
};
模板文件:
<!-- ./src/html/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<h1>Hello Webpack</h1>
</body>
</html>
🧠 类比:
HtmlWebpackPlugin就像“上架机器人”, 每次出货(打包)后,它会自动:
- 生成一个 HTML 页面;
- 把 JS 文件插进去;
- 自动帮你布置好展示页。
🧹 2. CleanWebpackPlugin
打包前清空旧的 dist 目录,防止文件堆积。
安装:
npm install --save-dev clean-webpack-plugin
使用:
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
plugins: [new CleanWebpackPlugin()]
};
🧠 类比:
“清洁工”插件,在工厂开工前打扫干净旧材料, 避免新旧产品混在一起。
🧩 六、总结口诀法
Loader 处理单个文件,Plugin 管理整个过程。
| 类型 | 功能 | 类比 |
|---|---|---|
| Loader | 把文件翻译成 JS 模块 | 厨房里的厨师 |
| Plugin | 管理打包生命周期 | 工厂里的主管 |
📘 一句话背完:
Loader 做加工,Plugin 管流程; HTML 自动上架,Clean 打扫仓。 Define 注入环境,Hot 热更新忙。 Compress 减肥瘦,Banner 贴标签。 分离 CSS 漂亮妆,Copy 文件去搬场。 🎯
🧱 一、插件进阶(第31–33页)
这一部分讲了几个非常常用的 Webpack 插件(Plugin) 。 我们继续用“工厂流水线”的比喻来记:
Webpack 就像一个“自动饮料工厂 🍹”, Loader 是机器臂(加工每种原料), Plugin 是总管(在不同阶段插手安排任务)。
✅ 6.3.3 mini-css-extract-plugin
👉 作用:把 CSS 独立抽取到单独文件中。
安装:
npm install --save-dev mini-css-extract-plugin
配置:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
{
test: /.s[ac]ss$/,
use: [
{ loader: MiniCssExtractPlugin.loader },
'css-loader',
'sass-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({ filename: '[name].css' })
]
};
🧠 类比理解:
原本所有样式都“打包进 JS”里(style-loader 注入
<style>), 而现在工厂主管说:“把样式单独装到一个文件袋子里(.css 文件)方便复用。” 这样就能让 CSS 独立加载,更快缓存、提高性能。
⚙️ 6.3.4 DefinePlugin
👉 作用:定义全局变量,在编译时可直接注入。
const { DefinePlugin } = require('webpack');
module.exports = {
plugins: [
new DefinePlugin({
BASE_URL: JSON.stringify("./") // 必须是字符串!
})
]
};
在 HTML 或 JS 中使用:
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
🧠 类比理解:
DefinePlugin 就像“工厂参数面板”🧩, 在出货前,你给它一个“常量标签”。 所有文件打包时会自动替换成真实路径或变量值。
✅ 例如:
if (process.env.NODE_ENV === 'production') { ... }
就是通过 DefinePlugin 注入的。
📁 6.3.5 copy-webpack-plugin
👉 作用:复制文件或目录到打包输出目录(dist)。
安装:
npm install copy-webpack-plugin -D
配置:
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: 'public', // 源目录
to: 'dist', // 目标目录(可省略)
globOptions: {
ignore: ['**/index.html'] // 忽略某些文件
}
}
]
})
]
};
🧠 类比理解:
这个插件就像“工厂搬运工 🧺”, 帮你把 public 目录的文件搬到出货仓库(dist)中。 常见于 Vue、React 等项目,用来复制 favicon、静态图片等。
⚖️ 二、Loader 与 Plugin 的区别(第34页)
Webpack 的“工厂模型”到这里形成了完整闭环👇
| 类型 | 负责阶段 | 主要作用 | 类比 |
|---|---|---|---|
| Loader | 文件级别 | 加工文件(把不同格式转成 JS) | 厨师 🍳 |
| Plugin | 全流程 | 参与构建的生命周期(优化、清理、注入变量) | 主管 👨🏭 |
📊 图示理解
entry → loaders → output
↑
plugins(贯穿全流程)
📘 区别总结:
| 区别点 | Loader | Plugin |
|---|---|---|
| 执行时机 | 编译文件前 | 编译生命周期各阶段 |
| 处理对象 | 文件内容(如 .css, .png) | 构建结果或流程本身 |
| 作用方式 | 文件级转换器 | 生命周期扩展器 |
| 编写目标 | 改变文件内容 | 控制 Webpack 行为 |
🧠 类比:
Loader 就像“做饭的厨师”🍳 Plugin 就像“餐厅经理”👨💼, 前者处理食材(代码),后者控制整个营业流程(打包策略)。
🧰 三、编写 Loader(第35页)
自己写一个 Loader,其实就是写一个 JS 函数。
🪄 1️⃣ 基本结构
module.exports = function (source) {
// source 就是被加载的文件内容
console.log('🎯 自定义 loader 运行中...');
return source.replace('老铁', '兄弟');
};
在 webpack.config.js 中配置:
module.exports = {
module: {
rules: [
{
test: /.txt$/,
use: path.resolve(__dirname, 'my-loader.js')
}
]
}
};
🧠 类比:
这就像你造了一个“自定义搅拌机 🌀”, 每次遇到
.txt文件,Webpack 会把内容送进来加工后再输出。
🧩 2️⃣ Loader 进阶写法(使用 this.callback)
module.exports = function (source) {
const result = source.replace('hello', 'hi');
this.callback(null, result); // 异步返回结果
};
📘 说明:
this是 Webpack 注入的上下文对象;this.callback(error, result)是异步回调;- 返回的
result可以是字符串或 Buffer。
🔧 3️⃣ Loader 可以做什么?
✅ 文件内容转换:
.less → .css.ts → .js.md → .html
✅ 文件内容注入:
- 自动加前缀 / banner
- 压缩代码 / 删除注释
✅ 格式转换:
- 图片 → base64
- 文本 → 模板字符串
🧠 四、编写 Plugin 思路(预告)
Plugin 比 Loader 高级,它能在整个 Webpack 生命周期中插手。 下一步就是编写一个自定义 Plugin(第36页开始会讲)。
📌 最后记忆总结
| 对比点 | Loader | Plugin |
|---|---|---|
| 作用 | 处理文件 | 扩展功能 |
| 阶段 | 打包前 | 整个生命周期 |
| 输入 | 文件内容 | Webpack 编译对象 |
| 输出 | 转换后代码 | 影响构建或输出结果 |
| 举例 | css-loader, file-loader | HtmlWebpackPlugin, CleanWebpackPlugin |
💡 一句话顺口溜记忆:
Loader 改食材 🍖,Plugin 管工厂 🏭; Loader 在前加工,Plugin 在后调度。 前者改内容,后者改流程,双管齐下才叫真·打包王者!⚙️
🧩 一、编写自定义 Loader(第 36 页)
先记一句口诀:
Loader 负责“处理文件”,Plugin 负责“管流程”。
Loader 本质上就是一个函数,用来“接收文件 → 处理 → 返回处理后的结果”。
✅ 1️⃣ Loader 基本结构
module.exports = function (source) {
const content = doSomeThing2JsString(source); // 处理逻辑
const options = this.query; // 获取配置项
console.log('当前上下文:', this.context); // 打印上下文
this.callback(null, content); // 异步返回结果
return content; // 同步返回结果
};
🔍 详细解读:
source:被 webpack 读取的文件内容;doSomeThing2JsString(source):你自己写的“加工逻辑”,例如替换文本或压缩;this.query:webpack 配置中传入 loader 的 options;this.context:当前模块所在路径;this.callback(err, result):异步返回结果。
💡 生活例子理解:
Webpack 就像一个食品加工厂, 每个 Loader 就是一台“机器”。 你可以造一台机器,让它在加工前替换文本、压缩代码、或转换格式。
比如自定义一个“文本替换 loader”👇
// my-replace-loader.js
module.exports = function (source) {
return source.replace(/你好/g, 'Hello');
};
配置:
module.exports = {
module: {
rules: [
{
test: /.txt$/,
use: path.resolve(__dirname, 'my-replace-loader.js')
}
]
}
};
效果:
把所有文件中出现的“你好”都自动替换成 “Hello”。
⚙️ 二、编写自定义 Plugin(第 37 页)
Webpack 的 Plugin 是在“整个生命周期”中工作的, 比如:开始编译、写入文件、打包完成…… 你可以在这些阶段插入“自定义动作”。
✅ 1️⃣ 基本模板
class MyPlugin {
apply(compiler) {
compiler.hooks.emit.tap('MyPlugin', (compilation) => {
console.log('🎯 文件即将输出!');
// compilation 包含即将输出的资源
});
}
}
module.exports = MyPlugin;
🔍 解释:
apply(compiler)是每个插件的“入口”;compiler表示整个 webpack 的运行环境;compiler.hooks.emit.tap表示监听“emit(输出文件前)”这个阶段;compilation表示本次构建的上下文(你可以改它里面的资源内容)。
💡 生活类比:
Plugin 就像“工厂的总监 🧑🏭”。 他不处理具体原料(那是 Loader 的事), 他在不同环节下达命令:
- 文件快输出了 → 打印日志;
- 打包完了 → 自动清理旧文件;
- 写入前 → 压缩、加水印。
✅ 2️⃣ Plugin 内部核心对象
- compiler:全局对象,代表整个构建;
- compilation:一次打包的详细过程,包括所有模块、文件。
✅ 3️⃣ 生命周期钩子示意
| 阶段 | 时机 | 举例 |
|---|---|---|
| run | 开始编译 | 显示“开始打包” |
| compile | 创建编译对象 | 准备资源 |
| emit | 输出文件前 | 压缩、加 banner |
| done | 完成 | 打印“打包完成” |
⚡ 三、如何提升 Webpack 构建速度(第 38–40 页)
随着项目变大,Webpack 打包越来越慢。 所以我们需要优化构建速度。
✅ 8.2.1 优化 Loader 配置
module.exports = {
module: {
rules: [
{
test: /.js$/,
use: ['babel-loader?cacheDirectory'], // 开启缓存
include: path.resolve(__dirname, 'src') // 只处理 src
}
]
}
};
📘 优化点:
- include:只处理 src 下的文件;
- exclude:排除 node_modules;
- cacheDirectory:缓存上次编译结果,加快重复构建。
🧠 类比:
就像告诉工厂机器:“只加工自家原料,不去外面市场捡货。” 这样能少干很多活,速度翻倍。
✅ 8.2.2 合理使用 resolve.extensions
module.exports = {
resolve: {
extensions: ['.js', '.json']
}
};
📘 含义:
- Webpack 在解析文件时会自动补全后缀;
- 越多的后缀,查找越慢;
- 建议只写项目中常用的后缀。
🧠 类比:
你去仓库找“饮料瓶”,只查“.js”和“.json”两种箱子; 不用一个个箱子都打开(.jsx, .vue, .ts...),能快很多。
✅ 8.2.3 优化 resolve.modules
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'node_modules')]
}
};
📘 含义:
- 告诉 webpack 去哪里找第三方库;
- 默认是从当前目录往上层一层层找;
- 指定绝对路径后,能少爬目录,加快查找速度。
🧠 类比:
就像你告诉工厂主管:“所有原料都放在固定仓库 node_modules,不要去隔壁村找。”
✅ 8.2.4 优化 resolve.alias
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
}
};
📘 含义:
- 给路径取别名;
- 避免写
../../../components/Button.vue; - 写成
@/components/Button.vue。
🧠 类比:
给仓库的“储物区”起了外号,比如“@ = 原料区”, 工人不用走迷宫找文件,节省路径解析时间。
✅ 8.2.5 使用 DLLPlugin 插件
📘 DLL(动态链接库)是 Windows 下共享函数库的机制。 Webpack 借鉴这个概念,可以提前打包不常改动的第三方库(如 React、Vue)。
8.2.5.1 打包一个 DLL 库
// webpack.dll.config.js
const webpack = require('webpack');
module.exports = {
entry: {
vendor: ['vue', 'axios'] // 这些库提前打包成 DLL
},
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, 'dll'),
library: '[name]_lib'
},
plugins: [
new webpack.DllPlugin({
name: '[name]_lib',
path: path.resolve(__dirname, 'dll/[name]-manifest.json')
})
]
};
然后在主 webpack 配置中引入:
new webpack.DllReferencePlugin({
manifest: require('./dll/vendor-manifest.json')
});
🧠 类比:
DLL 就像“预制菜 🍱”。 你把不会变的库(Vue、React)先做好,放冰箱里。 之后打包新菜时,直接用现成的料,构建速度立刻起飞 🚀。
🧠 四、总结口诀记忆法
| 优化点 | 核心思路 | 类比记忆 |
|---|---|---|
| include / exclude | 限定加工范围 | “只处理自家原料” |
| cacheDirectory | 缓存中间结果 | “复用上次工作记录” |
| extensions | 精简后缀查找 | “少翻箱子” |
| modules | 指定模块路径 | “固定仓库位置” |
| alias | 路径简化 | “给仓库起外号” |
| DLLPlugin | 预打包依赖库 | “预制菜加速出餐” |
📘 一句话总结:
Webpack 优化就像“改进工厂生产线”: 少加工、多缓存、路短、预制、命名好,一切都会变快! ⚙️💨
🚀 一、Webpack 构建速度优化总结(第 41~43 页)
在这一部分,Webpack 就像是“打包工厂 🏭”, 我们要让工厂“更快出货”,方法主要有:
少干活 + 多用缓存 + 并行干活 + 合理调度。
✅ 8.2.5.2 引入 DLL 库(预打包依赖)
module.exports = {
plugins: [
new webpack.DllReferencePlugin({
context: path.resolve(__dirname, './dll'),
manifest: path.resolve(__dirname, './dll/vendor-manifest.json')
}),
new AddAssetHtmlPlugin({
outputPath: './auto',
filepath: path.resolve(__dirname, './dll/vendor.dll.js')
})
]
};
📘 作用:
- 把 Vue、React、Lodash 这类不常变的库提前“打包缓存”;
- 再次构建时直接使用缓存版本,而不用重新编译。
🧠 生活类比:
像餐厅把“炒饭底料”提前炒好放冰箱,下次来客人点菜直接加热,速度翻倍。
✅ 8.2.6 使用 cache-loader(缓存中间结果)
module.exports = {
module: {
rules: [
{
test: /.ext$/,
use: ['cache-loader', ...loaders],
include: path.resolve('src')
}
]
}
};
📘 含义:
- 把耗时的 Loader 结果(比如 Babel 转译)缓存到磁盘;
- 下次打包如果源码没变,直接用缓存,速度暴增。
⚠️ 建议:只加在性能开销大的 loader上,比如 babel-loader、ts-loader。
🧠 类比:
第一次打包时“记录下来加工过程”,下次直接复用,不用再干同样的活。
✅ 8.2.7 Terser 启动多线程压缩
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
parallel: true // 多进程同时压缩
})
]
}
};
📘 解释:
- 开启
parallel让 CPU 的多个核心同时工作; - 类似厨房多开几口锅同时炒菜 🍳。
✅ 8.2.8 合理使用 sourceMap
Webpack 的 devtool 决定调试信息的生成方式。 生成的越详细,打包越慢。
📊 表格总结(第 43 页)
| devtool | 构建速度 | 重建速度 | 生产环境可用 | 质量 |
|---|---|---|---|---|
| none | +++ | +++ | ✅ yes | 打包后代码 |
| eval | +++ | +++ | ❌ | 生成后代码 |
| cheap-eval-source-map | ++ | ++ | ❌ | 转换过代码 |
| cheap-module-source-map | o | o | ✅ | 原始源码 |
| source-map | -- | -- | ✅ | 原始源码(最清晰) |
🧠 结论记忆法:
开发时用 “cheap-eval-source-map”(快), 上线时用 “hidden-source-map”(安全,不暴露源码)。
⚡ 二、借助 Webpack 优化前端性能(第 44~45 页)
🧠 9.1 背景
前端项目越来越大,网页会变卡、加载慢。 Webpack 优化 = “打包变小 + 加载更快”。
核心目标:减少网络传输体积、提高浏览器渲染效率。
🧩 9.2 如何优化(总览)
通过 Webpack 可做的优化手段:
- ✅ JS 代码压缩(减少 JS 大小)
- ✅ CSS 压缩(减少 CSS 冗余空格、注释)
- ✅ HTML 压缩(去掉多余换行)
- ✅ 图片压缩
- ✅ Tree Shaking(去掉无用代码)
- ✅ 代码分离(按需加载)
- ✅ 公共模块提取(chunk 分组)
🧠 生活类比:
网页加载就像“出差打包行李 🧳”, 你要带的文件越少、行李越轻,速度就越快!
✅ 9.2.1 JS 代码压缩 — TerserPlugin
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimize: true, // 启用压缩
minimizer: [
new TerserPlugin({
parallel: true // 开启多线程
})
]
}
};
📘 参数详解:
| 参数 | 含义 |
|---|---|
parallel | 开启多进程,加快压缩速度 |
extractComments | 是否把注释提取出来 |
compress | 是否启用压缩(默认 true) |
mangle | 是否混淆变量名 |
toplevel | 是否压缩顶层作用域变量 |
keep_classnames | 保留类名 |
keep_fnames | 保留函数名 |
🧠 生活类比:
这就像“打包行李 + 真空压缩袋”, 把 JS 文件压到最小体积、删除多余注释,还能混淆代码防偷窥 👀。
✅ 9.2.2 CSS 代码压缩 — css-minimizer-webpack-plugin
npm install css-minimizer-webpack-plugin -D
配置:
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [new CssMinimizerPlugin()]
}
};
📘 原理:
- 去除空格、注释;
- 合并重复样式;
- 压缩选择器名;
- 常与 MiniCssExtractPlugin 一起用。
🧠 生活类比:
就像把衣服折叠、真空压缩、放小袋; 最后打包轻便、加载快、传输少。
🎯 三、Webpack 优化核心总结口诀(整合 41–45 页)
| 优化方向 | 工具 | 作用 | 类比 |
|---|---|---|---|
| JS 压缩 | terser-webpack-plugin | 代码更小、可混淆 | 真空压缩袋 |
| CSS 压缩 | css-minimizer-webpack-plugin | 精简样式 | 折叠衣物 |
| 图片压缩 | image-webpack-loader | 减少图片体积 | 压缩照片 |
| Tree Shaking | 内置功能 | 删除无用函数 | 扔掉没穿的衣服 |
| 代码分离 | SplitChunksPlugin | 按需加载模块 | 分类打包行李 |
| DLL 预构建 | DllPlugin | 缓存第三方库 | 预制菜提前做 |
| 缓存 Loader | cache-loader | 加快二次构建 | 复用记录 |
| SourceMap 控制 | devtool | 平衡调试和速度 | 调试开关 |
🧠 一句话速记法
Loader 负责加工,Plugin 负责加速。 压缩瘦身靠 Terser,缓存提速用 Cache。 DLL 预制菜先炒好,SourceMap 按场景开。 少查找、少打包,Webpack 飞起来!⚡
🍱 9.2.3 HTML 文件压缩(第 46 页)
module.exports = {
plugins: [
new HtmlWebpackPlugin({
minify: {
minifyCSS: false, // 是否压缩内联的 CSS
collapseWhitespace: false, // 是否折叠空格
removeComments: true // 是否移除注释
}
})
]
};
📘 讲解:
- 用
HtmlWebpackPlugin生成最终的index.html - 设置
minify属性即可开启压缩 - 实际上内部用的是
html-minifier-terser
💡 类比生活:
像做完设计稿后清理多余注释、删掉空白区域,让文件更干净,浏览器加载更快。
📦 9.2.4 文件大小压缩(Gzip 压缩)
npm install compression-webpack-plugin -D
new CompressionPlugin({
test: /.(css|js)$/, // 哪些文件需要压缩
threshold: 500, // 超过 500B 的文件才压缩
minRatio: 0.7, // 压缩比例,越小压得越狠
algorithm: "gzip" // 采用 gzip 算法
})
📘 讲解:
- Gzip 会在打包时生成
.gz文件; - 浏览器请求时会自动下载压缩后的版本。
🧠 生活类比:
像把衣服装进“真空压缩袋”,发货时更轻,快递费也省。
🖼️ 9.2.5 图片压缩(第 48 页)
module: {
rules: [
{
test: /.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: { name: '[name]_[hash].[ext]', outputPath: 'images/' }
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: { quality: 65 },
optipng: { enabled: false },
pngquant: { quality: '65-90', speed: 4 },
gifsicle: { interlaced: false },
webp: { quality: 75 }
}
}
]
}
]
}
📘 讲解:
image-webpack-loader能帮你智能压缩图片体积;- 不会明显损坏画质;
- 还可以转成更小的 WebP 格式。
🧠 生活类比:
像 Photoshop 导出网页图片时选“压缩优化”; 文件小一半,但肉眼看几乎一样。
🌳 9.2.6 Tree Shaking(第 49 页)
Tree Shaking = 摇掉没用的“枯枝叶 🌿”,只留下真正被用到的函数。
✅ 原理
它基于 ES Module 的静态分析能力(import/export),能自动识别没被使用的代码并删除。
9.2.6.1 usedExports
module.exports = {
optimization: {
usedExports: true
}
};
📘 解释:
- Webpack 会标记未使用的导出函数;
- 在压缩(Terser)阶段自动清除。
🔍 举例:
// utils.js
export function sum(a, b) { return a + b }
export function sub(a, b) { return a - b }
// main.js
import { sum } from './utils.js'
console.log(sum(1,2))
👉 打包后只会保留 sum,sub 会被删除。
9.2.6.2 sideEffects
"sideEffects": [
"./src/util/format.js",
"*.css"
]
📘 解释:
- 告诉 Webpack 哪些文件“有副作用”不能删除;
- 其他模块都可安全摇掉。
🧠 类比:
就像大扫除时标记“别扔我 🚫”的东西,其他的都可以清理掉。
9.2.6.3 CSS Tree Shaking
npm install purgecss-webpack-plugin -D
const PurgeCssPlugin = require('purgecss-webpack-plugin');
const glob = require('glob');
module.exports = {
plugins: [
new PurgeCssPlugin({
path: glob.sync(`${path.resolve('./src')}/**/*`, { nodir: true }),
safelist: () => ({ standard: ['html'] })
})
]
};
📘 讲解:
purgecss-webpack-plugin会扫描你的 HTML / JS;- 删除没被使用的 CSS 样式;
- 类似 “把没穿的衣服扔掉”。
🧠 类比:
页面没用到的
.btn-large、.old-style等样式,全都被清理掉, 就像衣柜断舍离 👕。
✂️ 9.2.7 代码分离(第 50 页)
将项目的 JS 拆分成多个独立包,按需加载,页面初始加载更快。
📘 Webpack 实现方式:
- 使用
SplitChunksPlugin - 或者手动
import()动态加载。
// 动态加载示例
button.addEventListener('click', () => {
import('./math.js').then(({ add }) => {
console.log(add(2, 3))
})
});
🧠 类比:
不用一次带齐所有行李,到了需要的时候再叫外卖一样“懒加载”。
🎯 快速记忆口诀
| 优化方向 | 插件 / 配置 | 作用 | 生活类比 |
|---|---|---|---|
| HTML 压缩 | HtmlWebpackPlugin | 清除空格注释 | 整理文档 |
| 文件压缩 | CompressionPlugin | gzip 压缩 | 真空打包 |
| 图片压缩 | image-webpack-loader | 降低图片体积 | PS 导出优化 |
| Tree Shaking | usedExports + sideEffects | 删除无用代码 | 大扫除扔垃圾 |
| CSS Tree Shaking | PurgeCssPlugin | 删除没用样式 | 衣柜断舍离 |
| 代码分离 | SplitChunksPlugin / import() | 按需加载 | 外卖式加载 |
💡 一句话总结:
Webpack 优化三板斧: “压缩体积、摇掉无用、分离按需” 。 让前端加载像“轻装出行”,又快又省!
🚀 一、代码分离与内联 chunk(第 51 页)
✅ 9.2.7 代码分离
目的:让页面加载更快,不要一次加载所有 JS。
默认情况下,webpack 会把所有业务代码 + 第三方库都打包到一个文件里(太大)。 所以要“拆包”👉 按需加载(例如首页只加载首页代码)。
module.exports = {
optimization: {
splitChunks: {
chunks: 'all'
}
}
};
📘 解释:
chunks: "all"表示无论同步 / 异步代码都参与拆包;- webpack 会自动识别重复代码提取成公共模块。
🧠 类比:
就像打包行李:不要一个大箱子塞完所有物品,而是分成“日用品箱”、“电子设备箱”、“衣物箱”,用时再打开相应的箱子。
✅ 9.2.8 内联 chunk(Inline Chunk)
某些代码(如 runtime)体积小但必须加载,所以干脆内联进 HTML。
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/])
]
};
📘 解释:
- runtime 是 webpack 自己的运行时;
- 直接写进 HTML 里能减少一次 HTTP 请求。
🧠 类比:
就像你在饭盒里直接放筷子,不用再单独打包一根筷子袋。
🧩 二、Webpack 优化总结(第 52 页)
webpack 的性能优化主要从两条线出发:
- 📦 文件体积优化(压缩、Tree Shaking、拆包)
- ⚡ 加载速度优化(缓存、按需加载、HTTP 请求减少)
🧠 一句话总结:
少带行李 + 提前打包 + 只拿需要的。
🔧 三、与 webpack 类似的构建工具(第 52~55 页)
Webpack 并不是唯一的打包器。 我们还常见 Rollup、Parcel、Snowpack、Vite。
🔹 模块化工具是什么?
模块化 = 把复杂项目拆成独立“零件”,方便组合和维护。
比如:
logger.js负责打印日志messages.js负责文本index.js把它们组装成完整应用
这就叫模块化开发。
✅ 10.1.1 Rollup(第 53–54 页)
Rollup 是一个 专注于 ES Module 打包 的轻量工具, 常用于打包库,比如 Vue、React、Three.js 都用它。
📘 示例讲解:
// ./src/messages.js
export default { hi: 'Hey Guys, I am zce' };
// ./src/logger.js
export const log = msg => {
console.log('-------- INFO --------');
console.log(msg);
console.log('----------------------');
};
export const error = msg => {
console.error('-------- ERROR --------');
console.error(msg);
console.error('-----------------------');
};
// ./src/index.js
import { log } from './logger';
import messages from './messages';
log(messages.hi);
打包命令:
npx rollup ./src/index.js --file ./dist/bundle.js
打包结果:
const log = msg => {
console.log('-------- INFO --------');
console.log(msg);
console.log('----------------------');
};
var messages = { hi: 'Hey Guys, I am zce' };
log(messages.hi);
📘 优点:
- 输出代码干净无多余模块包装;
- 默认支持 Tree Shaking;
- 打包体积更小,适合做 npm 库。
📘 缺点:
- 不支持 HMR(热更新);
- 不支持 CommonJS;
- 第三方生态没 webpack 丰富。
🧠 生活类比:
Rollup 就像“便携炊具”🍳——适合做轻便的小餐(库), 而 Webpack 是“大型厨房”👨🍳——适合多菜一锅的项目(前端应用)。
✅ 10.1.2 Parcel(第 55 页)
Parcel 是一款“傻瓜式打包器”,几乎零配置! 你甚至不用写 webpack.config.js,它能自动识别依赖。
📘 示例:
📄 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Parcel Tutorials</title>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
📄 main.js
import { log } from './logger';
log('hello parcel');
📄 logger.js
export const log = msg => {
console.log('-------- INFO --------');
console.log(msg);
};
运行命令:
npx parcel src/index.html
📘 Parcel 会自动:
- 打包文件;
- 启动开发服务器;
- 自动安装缺失的依赖;
- 支持模块热替换(HMR)。
🧠 类比:
Parcel 就像“自动料理锅 🍲”, 你只放食材,它会自动加热、调味、装盘。 而 webpack 像专业厨师,需要你手动设置锅温、火候、油量。
🔚 总结对比表
| 工具 | 特点 | 适用场景 |
|---|---|---|
| Webpack | 功能最全、生态最强、配置复杂 | 大型项目、企业级应用 |
| Rollup | 输出最干净、适合打包库 | 打包 npm 库、JS SDK |
| Parcel | 零配置、上手快 | 小型项目、快速原型 |
| Vite | 极速热更新、原生 ES Module | Vue3 / React 新项目 |
| Snowpack | 静态导入、冷门了 | 曾用于现代化实验项目 |
💡 一句话总结
⚙️ Webpack 像「工厂流水线」 🧰 Rollup 像「轻便工作台」 🍲 Parcel 像「自动料理锅」 🚀 Vite 像「闪电厨房(即时加热)」
🧩 一、Parcel 打包效果与特点(第 56 页)
📦 打包命令
npx parcel src/index.html
执行后 Parcel 会:
- 自动检测依赖、打包;
- 启动本地开发服务器;
- 自动支持 HMR(热更新)。
📂 打包结果
dist/
├── index.html
├── jquery.93353cef.js
├── jquery.93353cef.js.map
├── main.6c885ac1.js
├── main.6c885ac1.js.map
└── main.f309d2b9.css
📘 特点:
- 输出的文件被自动压缩、带 hash;
.map文件方便调试;- 无需配置 webpack.config.js;
- 支持多进程构建,比 webpack 构建更快。
🧠 生活比喻:
Parcel 就像“全自动咖啡机 ☕”, 你只放咖啡豆(源码),它帮你磨粉、萃取、加奶、拉花,一气呵成。
⚡ 二、Snowpack(第 56~57 页)
Snowpack 是“闪电级”的前端构建工具,专为现代浏览器设计。 它是 webpack / parcel 的替代方案,不需要每次改动都重新打包整个项目。
💡 工作方式对比
| 工具 | 打包方式 | 改动时反应速度 |
|---|---|---|
| Webpack | 打包整个 bundle | 每次改动都重编译所有文件(慢) |
| Snowpack | 按文件缓存编译 | 改动哪个文件只重新构建那一个(快) |
📊 视觉对比(来自图):
Webpack:
build 50ms → bundle 1+秒 → file change → rebuild 1+秒
Snowpack:
build 50ms → file change → build 50ms (无重新打包)
🧠 生活类比:
webpack 像传统饭店:每改菜单一道菜就得重开炉子。 Snowpack 像自助餐厅:你改一道菜,只做那道菜,不动其他。
⚙️ 三、Vite(第 57~58 页)
Vite 是新时代的前端构建利器,由 Vue 作者尤雨溪开发。
📘 核心理念
Vite 由两部分组成:
- 一个开发服务器 —— 速度极快、原生支持 HMR;
- 一套打包方案(基于 Rollup)—— 用于生产构建。
🧠 原理图(来自第 58 页)
1. 浏览器请求 module2
2. devServer 动态编译该模块
3. 返回编译结果给浏览器显示
📘 解释:
- Vite 基于 ES Module 原生导入;
- 浏览器请求哪个文件,Vite 只编译哪个;
- 不会像 webpack 一样重新打包整个项目。
🧠 生活比喻:
webpack 像先把整个餐厅菜单菜品全部做出来(大锅饭 🍲); Vite 像现点现做(你点菜我立刻炒 🥘)。
🧩 主要特性
- ⚡ 极速启动:不打包,直接运行;
- 🔥 即时模块热更新(HMR) ;
- 🧱 真实的按需编译;
- 🎯 基于 Rollup 输出高质量生产包;
- 🧰 原生支持 ES Modules,无需 loader 配置。
📘 示例
文件结构:
src/
├── main.js
├── App.vue
└── components/Hello.vue
启动命令:
npm run dev
Vite 立即启动服务器并编译首屏页面,不做任何预打包。 当你修改 Hello.vue,只重新刷新这一块组件。
🧠 生活比喻:
webpack 是“整栋房子重建”, Vite 是“只换坏掉的灯泡 💡”。
🧱 四、Webpack(第 58 页)
最后再看“老大哥” Webpack 的定位: 它功能最全、生态最强、配置最复杂。
✅ 优势总结
| 优势 | 说明 |
|---|---|
| 🧠 兼容性强 | 同时支持 CommonJS、AMD、ESM |
| 🧩 资源广泛 | 支持 JS、CSS、图片等各种资源打包 |
| 🔥 功能丰富 | 内置 HMR、Tree Shaking |
| ⚙️ 分包灵活 | 可自由拆分 chunk 实现按需加载 |
| 🧱 插件体系庞大 | plugin 接口强大,可扩展性高 |
| 🧭 调试完善 | SourceMap / devtool 支持 |
| ⚡ 性能优秀 | 支持缓存、多线程并行构建 |
| 🌍 生态成熟 | 问题好搜、社区活跃 |
🧠 生活比喻总结:
| 工具 | 比喻 | 特点 |
|---|---|---|
| Webpack | 多功能大型厨房 👨🍳 | 一切都能做,配置复杂但强大 |
| Rollup | 轻便便当厨房 🍱 | 打包 JS 库最强 |
| Parcel | 全自动料理机 🍲 | 零配置,上手快 |
| Snowpack | 冷启动微波炉 ⚡ | 改动不重做,速度极快 |
| Vite | 即点即做餐厅 🚀 | 极速 HMR,现代浏览器友好 |
🎯 一句话速记口诀
Webpack 老而全,Rollup 轻而精; Parcel 傻瓜快,Snowpack 秒响应; Vite 最新潮,开发爽如风。