20251010-Webpack八股文整理

128 阅读27分钟

🧩 一、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.jsbundle.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:

  1. 启动一个 本地服务器
  2. 页面运行时通过 WebSocket 与服务器保持通信;
  3. 一旦文件变化,服务器告诉浏览器;
  4. 浏览器只替换那部分模块,不刷新整页。

➡️ 就像“做奶茶时换了水果”,机器只替换水果,不重新冲整杯。


🧭 六、总结口诀

🌐 模块太乱 → 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:启动阶段

  1. webpack compile 把源码编译为 bundle.js
  2. 同时生成 HMR runtime(热更新运行时)
  3. 输出给 bundle server 静态服务器
  4. 浏览器加载页面,HMR runtime 也被注入到浏览器中

🔹 阶段2:更新阶段

  1. 当文件变化(比如 util.js) → Webpack 重新编译生成新的模块;

  2. 生成两个文件:

    • manifest.json(说明更新了哪些模块、hash值)
    • update.js(新模块代码)
  3. socket server 通知浏览器有更新;

  4. 浏览器拿到更新内容后,通过 HMR runtime 替换旧模块。


📦 类比记忆:

HMR 就像外卖系统:

  • HMR Server 是外卖后台;
  • socket 是骑手;
  • HMR runtime 是顾客的手机;
  • 当你换了一个食材(文件改动)时,后台(Webpack)重新打包一份新的配料;
  • 骑手(socket)马上送到顾客(浏览器),手机上自动替换掉旧的食材(模块)。

4️⃣ 具体运行过程

当浏览器和 socket server 建立连接后:

  1. 浏览器会收到包含 hash 值的更新信息。
  2. 浏览器根据 manifest.json 请求变化的模块。
  3. 新模块加载完成后执行替换(render 流程)。
  4. 整个页面无刷新更新成功 🎉

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
输出合并生成 bundlechunk + output

🌈 一句话记忆:

“Webpack 三步曲:读配置 → 编代码 → 出文件。”

🏭 一、Webpack 构建像一条“自动化工厂流水线”

想象 Webpack 就是一家「智能饮料工厂」🍹 你把各种原材料(JS / CSS / 图片)送进去,它会自动:

  1. 读取菜单(配置文件);
  2. 按配方混合(编译);
  3. 封装包装(打包输出);
  4. 把成品送上货架(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();
    });
  });
}

📘 解释步骤:

  1. 入口文件(entry)传进来;
  2. 找到合适的模块工厂(NormalModuleFactory);
  3. 创建一个空的 module
  4. 调用 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是否修改请求头里的 Hosttrue

🧱 二、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 → loadersoutput
  • 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解析 @importurl(),让 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 流程:

  1. css-loader → 读懂 CSS 内容;
  2. style-loader → 用 <style> 标签插入到 <head>
  3. 页面变粉 💗。

🎨 四、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 会:

  1. 把图片复制到 dist/images/logo_xxxxx.png
  2. 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 → loadersoutput
             ↑
         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 → loadersoutput
        ↑
     plugins(贯穿全流程)

📘 区别总结:

区别点LoaderPlugin
执行时机编译文件前编译生命周期各阶段
处理对象文件内容(如 .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页开始会讲)。


📌 最后记忆总结

对比点LoaderPlugin
作用处理文件扩展功能
阶段打包前整个生命周期
输入文件内容Webpack 编译对象
输出转换后代码影响构建或输出结果
举例css-loader, file-loaderHtmlWebpackPlugin, 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-loaderts-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-mapoo原始源码
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缓存第三方库预制菜提前做
缓存 Loadercache-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))

👉 打包后只会保留 sumsub 会被删除。


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 实现方式:

  1. 使用 SplitChunksPlugin
  2. 或者手动 import() 动态加载。
// 动态加载示例
button.addEventListener('click', () => {
  import('./math.js').then(({ add }) => {
    console.log(add(2, 3))
  })
});

🧠 类比:

不用一次带齐所有行李,到了需要的时候再叫外卖一样“懒加载”。


🎯 快速记忆口诀

优化方向插件 / 配置作用生活类比
HTML 压缩HtmlWebpackPlugin清除空格注释整理文档
文件压缩CompressionPlugingzip 压缩真空打包
图片压缩image-webpack-loader降低图片体积PS 导出优化
Tree ShakingusedExports + sideEffects删除无用代码大扫除扔垃圾
CSS Tree ShakingPurgeCssPlugin删除没用样式衣柜断舍离
代码分离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 ModuleVue3 / 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

📘 特点:

  1. 输出的文件被自动压缩、带 hash;
  2. .map 文件方便调试;
  3. 无需配置 webpack.config.js;
  4. 支持多进程构建,比 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 由两部分组成:

  1. 一个开发服务器 —— 速度极快、原生支持 HMR;
  2. 一套打包方案(基于 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 最新潮,开发爽如风。