banner
NEWS LETTER

前端小白的 Webpack 扫盲指南

Scroll down

Webpack官网图片

前置知识

在正式开始之前,我们需要先理解几个基础概念。如果你已经很熟悉,可以直接跳到第一部分。

0.1 什么是”模块化”?

问题场景: 假设你在写一个网站,有很多功能:日期格式化、数据校验、Ajax 请求等。如果把所有代码都写在一个 main.js 文件里,这个文件可能有几千行,根本没法维护!

模块化的思路: 把功能拆分成多个小文件,每个文件负责一件事:

1
2
3
4
5
6
项目文件夹/
├── utils/
│ ├── date.js ← 只负责日期处理
│ ├── validator.js ← 只负责数据校验
│ └── http.js ← 只负责网络请求
└── main.js ← 主文件,把上面的工具都引进来用

main.js里使用这些模块:

1
2
3
4
5
6
7
// ES6 模块化语法(现代项目都在用)
import { formatDate } from "./utils/date.js";
import { validateEmail } from "./utils/validator.js";

// 现在可以直接使用导入的功能了
console.log(formatDate(new Date())); // 输出: 2025-11-29
console.log(validateEmail("[email protected]")); // 输出: true

💡 记住: 模块化 = 把代码拆分成多个文件,让每个文件职责单一,方便复用和维护。


0.2 什么是”依赖”?

简单理解: 如果 A 文件需要用到 B 文件的功能,我们就说 “A 依赖 B”

举例:

1
2
3
4
5
6
7
8
9
// main.js
import Vue from "vue"; // main.js 依赖 vue
import App from "./App.vue"; // main.js 依赖 App.vue
import router from "./router"; // main.js 依赖 router

new Vue({
router,
render: (h) => h(App),
}).$mount("#app");

依赖链: main.js 依赖 App.vue,而 App.vue 可能又依赖 Header.vueFooter.vue。 这就像一棵树,从 main.js 这个树根,可以找到整个项目需要的所有文件。


0.3 什么是”构建工具”?

问题: 浏览器只认识三种文件:

  • .html (网页结构)
  • .css (样式)
  • .js (JavaScript 代码)

但现代前端开发,我们会用:

  • .vue 文件 (Vue 单文件组件)
  • .scss 文件 (SCSS 样式)
  • TypeScript (比 JS 更严格的语言)
  • import/export 语法 (ES6 模块化,老浏览器不支持)

构建工具的作用: 把这些浏览器看不懂的东西 “翻译 + 打包” 成浏览器能运行的纯 HTML、CSS、JS 文件。

常见的构建工具:

  • Webpack ← 今天的主角,功能强大但配置复杂
  • Vite ← 新一代工具,速度更快
  • Rollup ← 专门打包 JS 库

0.4 什么是 npmpackage.json?

npm (Node Package Manager)

作用: 一个超大的”代码仓库”,里面有几百万个别人写好的工具包(比如 vueaxioslodash)。

你不需要自己写所有功能,直接用别人的轮子:

1
npm install axios   # 安装 axios 这个工具(用于发送网络请求)

package.json

作用: 记录你的项目用了哪些工具包,以及项目的基本信息。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "my-project",
"version": "1.0.0",
"scripts": {
"serve": "vue-cli-service serve", ← npm run serve 启动开发服务器
"build": "vue-cli-service build" ← npm run build 打包上线
},
"dependencies": {
"vue": "^2.6.14", ← 项目依赖 Vue.js 2.6.14 版本
"axios": "^1.2.0" ← 项目依赖 axios
}
}

💡 核心:

  • npm install = 根据 package.json 安装所有依赖
  • npm run serve = 启动开发服务器
  • npm run build = 打包项目,生成可部署的文件

0.5 前端项目的典型文件结构

当你看到一个 Vue 项目时,通常是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
my-vue-project/
├── node_modules/ ← npm 安装的所有依赖包(不用看,也别动!)
├── public/
│ └── index.html ← HTML 模板
├── src/ ← 你的源代码都在这里
│ ├── assets/ ← 静态资源(图片、字体等)
│ ├── components/ ← Vue 组件
│ ├── views/ ← 页面
│ ├── router/ ← 路由配置
│ ├── App.vue ← 根组件
│ └── main.js ← 入口文件(项目从这里开始运行)
├── dist/ ← 打包后的文件(运行 npm run build 后生成)
├── package.json ← 项目配置文件
└── vue.config.js ← Vue CLI 的配置文件(可选)

💡 记住这两个核心目录:

  • src/ = 你写代码的地方(开发环境)
  • dist/ = 打包后的文件(生产环境,部署到服务器的就是这些文件)

0.6 开发环境 vs 生产环境

概念 解释 命令
开发环境 你在本地写代码、调试的环境,代码有注释、有报错 npm run serve
生产环境 代码部署到服务器,用户真正访问的版本 npm run build
代码会被压缩、混淆,去掉注释,体积更小 生成的文件在 dist/

为什么要区分? 开发时需要清晰的报错信息,方便调试; 上线时需要极致的性能,代码越小加载越快。


第一部分:没有 Webpack 的黑暗时代

1.1 痛点一:文件管理混乱

假设我们在做一个普通的网页项目,没有任何构建工具。你的 index.html 可能是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<title>我的网站</title>
<link rel="stylesheet" href="css/reset.css" />
<link rel="stylesheet" href="css/common.css" />
<link rel="stylesheet" href="css/header.css" />
<link rel="stylesheet" href="css/footer.css" />
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<div id="app"></div>

<script src="js/jquery.js"></script>
<script src="js/utils.js"></script>
<script src="js/api.js"></script>
<script src="js/header.js"></script>
<script src="js/footer.js"></script>
<script src="js/main.js"></script>
</body>
</html>

问题来了

  • ❌ 如果 main.js 依赖 utils.js,你必须保证 utils.js 在前面加载,顺序一乱,整个网站崩溃
  • ❌ 浏览器要发起 11 个 HTTP 请求才能加载完页面,速度慢。
  • ❌ 如果你想删掉 header.js,你得手动去 HTML 里找到对应的 <script> 标签删掉,容易漏删

1.2 痛点二:浏览器看不懂”高级语法”

你可能想用这些:

  • ES6 模块化import { add } from './utils.js'
  • SCSS/Less:嵌套写样式,超方便
  • Vue 单文件组件.vue 文件三件套 (template + script + style)

但浏览器会告诉你:

  • ❌ 老版本浏览器看不懂 ES6 的import
  • ❌ 浏览器不认识.scss 文件,只认识 .css
  • ❌ 浏览器不认识 .vue 文件,只认识 .html + .js + .css

1.3 我们需要一个”翻译 + 打包”工具!

这时候,Webpack 就登场了!

它就像一个智能快递打包站

  1. 收集:把你的所有文件(JS、CSS、图片、字体)统统收进来。
  2. 翻译:把浏览器看不懂的东西翻译成它能理解的(SCSS → CSS,ES6 → ES5,.vue → JS)。
  3. 打包:把几百个零散文件合并成几个大文件(减少 HTTP 请求)。
  4. 压缩:把代码压缩到极致,加载更快。
  5. 出货:把处理好的文件丢到 dist 目录,直接部署到服务器。

第二部分:Webpack 的核心概念

Webpack 的配置文件虽然长,但核心只有 4 个概念。我们一个一个拆解。

2.1 Entry (入口) —— 快递站的收货口

作用:告诉 Webpack 从哪个文件开始”抓取”依赖。

🏭 比喻:就像快递站的 “收货口” 。你把主包裹扔进去,Webpack 会自动顺着这个包裹的”地址标签”(import/require),把所有相关的包裹(依赖文件)都找出来。

示例

1
2
3
4
// webpack.config.js
module.exports = {
entry: "./src/main.js", // 从这个文件开始打包
};

当 Webpack 看到 main.js 里写了:

1
2
3
import Vue from "vue";
import App from "./App.vue";
import router from "./router";

它就会自动去找 vueApp.vuerouter 这些文件,一层一层往下找,形成一个依赖树

2.2 Output (出口) —— 快递站的出货口

作用:告诉 Webpack 打包好的文件放到哪里、叫什么名字。

🏭 比喻:就像快递站的 “出货口”。打包好的东西要丢到哪个仓库(文件夹)、贴什么快递单号(文件名)。

示例

1
2
3
4
5
6
7
8
9
10
// webpack.config.js
const path = require("path");

module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "dist"), // 输出到 dist 文件夹
filename: "bundle.js", // 打包后的文件名叫 bundle.js
},
};

运行 npm run build 后,你会看到生成了 dist/bundle.js,这就是所有代码合并压缩后的结果。

2.3 Loader (加载器) —— 翻译官( 核心)

作用:把 Webpack 原生看不懂的文件”翻译”成它能理解的 JavaScript 模块。

🏭 比喻:Webpack 本身只认识 JavaScript 和 JSON。就像一个只会说中文的快递员,遇到英文包裹、日文包裹就傻眼了。这时候需要**“翻译官”(Loader)** 来帮忙。

常见的 Loader

文件类型 需要的 Loader 作用
.css css-loader, style-loader 把 CSS 转成 JS 模块,并插入到页面
.scss/.sass sass-loader, css-loader, style-loader 先把 SCSS 翻译成 CSS,再处理成 JS
.vue vue-loader 把 Vue 单文件组件拆成 HTML、JS、CSS
.jpg/.png file-loaderurl-loader 处理图片文件
ES6/ES7 babel-loader 把新语法翻译成老浏览器能看懂的 ES5

示例配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module.exports = {
module: {
rules: [
{
test: /\.css$/, // 匹配所有 .css 文件
use: ["style-loader", "css-loader"], // 使用这两个 loader 处理
// 注意:执行顺序是从右到左,先 css-loader 再 style-loader
},
{
test: /\.scss$/,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.vue$/,
use: "vue-loader",
},
],
},
};

💡 记忆技巧

  • 遇到报错说 “You may need an appropriate loader”,就是在提示你:缺翻译官了!
  • 去 npm 搜 [文件类型]-loader,比如 less-loaderts-loader

2.4 Plugin (插件) —— 魔法道具

作用:做 Loader 做不到的事情。Loader 只负责”翻译文件”,Plugin 可以做更高级的事。

🏭 比喻:Loader 是”翻译官”,Plugin 是”魔法道具”。

常见的 Plugin

Plugin 作用
HtmlWebpackPlugin 自动生成 index.html,并自动引入打包后的 JS 文件
CleanWebpackPlugin 每次打包前先清空 dist 文件夹(避免旧文件堆积)
MiniCssExtractPlugin 把 CSS 从 JS 中抽离出来,生成单独的 .css 文件
DefinePlugin 定义全局常量(比如 process.env.NODE_ENV

示例配置

1
2
3
4
5
6
7
8
9
10
11
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
plugins: [
new CleanWebpackPlugin(), // 打包前清空 dist
new HtmlWebpackPlugin({
template: "./public/index.html", // 基于这个模板生成 HTML
}),
],
};

第三部分:结合 Vue 项目

3.1 Vue CLI 帮我们做了什么?

当你运行 vue create my-project 时,Vue CLI 已经帮你配置好了 Webpack 的一切。

你根本不需要写 webpack.config.js,而是通过 vue.config.js 来自定义配置。

让我们看看你们现在的项目:

运行构建命令

1
npm run build

你会看到终端输出:

1
2
3
4
5
⠹ Building for production...
File Size Gzipped
dist/js/chunk-vendors.js 100.23 kb 35.12 kb
dist/js/app.js 15.67 kb 4.89 kb
dist/css/app.css 2.34 kb 0.87 kb

打开 dist 文件夹,你会看到:

1
2
3
4
5
6
7
dist/
├── css/
│ └── app.a3f2b1c8.css
├── js/
│ ├── app.d7e4f5a6.js
│ └── chunk-vendors.b8c9d0e1.js
└── index.html

这就是 Webpack 做的事

  1. 把你写的所有 .vue.js.css 文件打包成了几个文件。
  2. 文件名后面的 a3f2b1c8哈希值,每次代码改变就会变,这样可以避免浏览器缓存旧代码。
  3. 自动生成了 index.html,并且自动引入了打包后的 JS 和 CSS。

3.2 看看 vue.config.js(如果有的话)

如果你的项目里有 vue.config.js,打开看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module.exports = {
publicPath: "./", // 打包后的资源路径
outputDir: "dist", // 输出目录

// 自定义 Webpack 配置
configureWebpack: {
resolve: {
alias: {
"@": path.resolve(__dirname, "src"), // 设置 @ 指向 src 目录
},
},
},

devServer: {
port: 8080, // 开发服务器端口
open: true, // 自动打开浏览器
},
};

如果是 History 模式路由(即 URL 里没有 # 号),用相对路径 ./ 会导致多级路由刷新后资源 404。这里通常要写绝对路径 / 或者子目录 /my-app/,否则刷新会挂

这就是在配置底层的 Webpack!

比如 resolve.alias 让你可以这样写:

1
2
3
// 不用写相对路径
import HelloWorld from "@/components/HelloWorld.vue";
// 而不是 import HelloWorld from '../../components/HelloWorld.vue'

第四部分:实战案例

首先明确一点:我们在 Vue 2 脚手架项目中,不会直接去改 webpack.config.js,而是去根目录找 vue.config.js。 “vue.config.js 就是我们控制 Webpack 的遥控器。

请注意:按需添加,不要为了配置而配置


案例一:解决“跨域”报错(DevServer)

痛点描述

“兄弟,你写登录功能时有没有遇到过这种情况?前端在 localhost:8080,后端接口在 localhost:3000。 你一发请求,浏览器控制台直接飘红,报一个 CORS 错误。你以为是你代码写错了,其实是浏览器拦截了。”

Webpack 解决方案: 利用 Webpack 的开发服务器(DevServer)做一个代理(Proxy)。相当于让 Webpack 在中间当“中介”,骗过浏览器。

代码实例 (vue.config.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports = {
// 这是 Webpack 的 devServer 配置
devServer: {
// 开启代理
proxy: {
// 只要请求路径是以 /api 开头的
"/api": {
target: "http://localhost:3000", // 告诉 Webpack,后端真的在哪里
changeOrigin: true, // 允许跨域
pathRewrite: {
"^/api": "", //以此去掉 /api 前缀(如果后端接口不需要这个前缀的话)
},
},
},
},
};

案例二:拒绝“…/…/…/…/”地狱(Alias 别名)

痛点描述

“你的项目结构一旦变深,引入一个组件是不是得写成这样? import MyButton from '../../../../components/MyButton.vue' 这简直太丑了,而且万一你把文件移动了一下,这行代码直接报错,找都不好找。”

Webpack 解决方案: 利用 Webpack 的 resolve.alias 功能,给路径起个“外号”。

代码实例 (vue.config.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const path = require("path");
function resolve(dir) {
return path.join(__dirname, dir);
}

module.exports = {
// 这里的 chainWebpack 是 Vue CLI 提供修改 Webpack 内部配置的方法
chainWebpack: (config) => {
// 设置别名
config.resolve.alias
// 以后 @ 就代表 src 目录(Vue CLI 默认已配置,但这告诉你是怎么来的)
.set("@", resolve("src"))
// 以后 _c 就代表 src/components 目录
.set("_c", resolve("src/components"))
// 以后 _v 就代表 src/views 目录
.set("_v", resolve("src/views"));
},
};

使用效果: 以后在任何文件里,你都可以直接写: import MyButton from '_c/MyButton.vue'


案例三:全局样式变量(Loader 配置)

痛点描述

“比如我们在写 Less 或 Sass。你定义了一个 variable.less 文件,里面存了主题色 @main-color: red;。 结果你在每个 .vue 组件里要用这个颜色时,都得手动 @import 一次这个文件,是不是巨烦?”

Webpack 解决方案: 修改 less-loadersass-loader 的配置,让它自动把这个变量文件“注入”到每一个组件里。

代码实例 (vue.config.js)

1
2
3
4
5
6
7
8
9
10
11
12
module.exports = {
css: {
loaderOptions: {
// 给 less-loader 传递选项
less: {
// 这里就是在配置 Webpack 的 Loader
// 自动给每个 less 文件头部引入 global.less
additionalData: `@import "@/assets/css/global.less";`,
},
},
},
};

⚠️ 注意:如果你是接手的老项目,如果 additionalData 报错,请尝试改成 prependData。这是 Webpack Loader 版本不同导致的。

第五部分:常见问题与避坑指南

Q1: 报错 “Module not found: Error: Can’t resolve ‘xxx’”

原因

  1. 文件路径写错了。
  2. 缺少对应的 Loader(比如你在 JS 里 import './style.scss',但没装 sass-loader)。

解决办法

  • 检查路径是否正确。
  • 如果是特殊文件类型,去装对应的 Loader:

Q2: 报错 “You may need an appropriate loader to handle this file type”

原因:Webpack 遇到了它不认识的文件类型,需要 Loader。

解决办法

  • 看报错信息提示的是什么文件(.vue? .ts? .less?)。
  • 去 npm 搜对应的 Loader 并安装。

Q3: 我需要从零手写 Webpack 配置吗?

答案:初学者千万不要!

理由

  • 一个完整的 Webpack 配置文件可能有几百行,配置项非常多。
  • 99% 的情况下,脚手架(Vue CLI、Create React App、Vite)已经帮你配好了。
  • 你只需要理解概念,会改脚手架的配置就够了。

学习路径

  1. 理解 Entry、Output、Loader、Plugin 四大概念。
  2. 学会看懂报错信息(通常都会提示缺什么 Loader)。
  3. 需要自定义时,去查 Vue CLI 或脚手架的官方文档。

总结:记住这 4 句话

  1. Entry (入口):从哪个文件开始打包?
  2. Output (出口):打包后的文件放哪里、叫什么名字?
  3. Loader (加载器):遇到浏览器不认识的文件,找对应的 Loader 翻译。
  4. Plugin (插件):Loader 干不了的高级活(清空目录、生成 HTML、压缩代码等)。

最后一句话送给大家

Webpack 不是用来背的,而是用来理解的。 当你理解了它在帮你解决什么问题,报错就不再可怕了。


🎁 额外资源(了解)


谢谢大家阅读!有问题可以随时提问!

其他文章
目录导航 置顶
  1. 1. 前置知识
  2. 2. 0.1 什么是”模块化”?
  3. 3. 0.2 什么是”依赖”?
  4. 4. 0.3 什么是”构建工具”?
  5. 5. 0.4 什么是 npm 和 package.json?
  6. 6. npm (Node Package Manager)
  7. 7. package.json
  8. 8. 0.5 前端项目的典型文件结构
  9. 9. 0.6 开发环境 vs 生产环境
  10. 10. 第一部分:没有 Webpack 的黑暗时代
  11. 11. 1.1 痛点一:文件管理混乱
  12. 12. 1.2 痛点二:浏览器看不懂”高级语法”
  13. 13. 1.3 我们需要一个”翻译 + 打包”工具!
  14. 14. 第二部分:Webpack 的核心概念
  15. 15. 2.1 Entry (入口) —— 快递站的收货口
  16. 16. 2.2 Output (出口) —— 快递站的出货口
  17. 17. 2.3 Loader (加载器) —— 翻译官( 核心)
  18. 18. 2.4 Plugin (插件) —— 魔法道具
  19. 19. 第三部分:结合 Vue 项目
  20. 20. 3.1 Vue CLI 帮我们做了什么?
  21. 21. 运行构建命令
  22. 22. 3.2 看看 vue.config.js(如果有的话)
  23. 23. 第四部分:实战案例
  24. 24. 案例一:解决“跨域”报错(DevServer)
  25. 25. 案例二:拒绝“…/…/…/…/”地狱(Alias 别名)
  26. 26. 案例三:全局样式变量(Loader 配置)
  27. 27. 第五部分:常见问题与避坑指南
  28. 28. Q1: 报错 “Module not found: Error: Can’t resolve ‘xxx’”
  29. 29. Q2: 报错 “You may need an appropriate loader to handle this file type”
  30. 30. Q3: 我需要从零手写 Webpack 配置吗?
  31. 31. 总结:记住这 4 句话
  32. 32. 🎁 额外资源(了解)