Webpack 详细教程:从入门到精通
目录
- 什么是 Webpack?
- 为什么需要 Webpack?
- 核心概念
- 第一个 Webpack 项目:零配置
- 手动配置 Webpack (核心步骤)
- 1. 项目初始化
- 2. 安装 Webpack
- 3. 创建
src目录和入口文件 - 4. 配置
webpack.config.js - 5. 配置打包脚本
- 6. 运行打包
- 处理样式文件
- 1. 加载 CSS
- 2. 提取 CSS 到单独文件
- 处理图片和其他文件
- 使用 Babel 转换 JavaScript
- 使用 Source Maps
- 开发服务器
- 高级优化
- 1. 生产环境与开发环境分离
- 2. 代码分割
- 3. 懒加载
- 4. Tree Shaking (摇树优化)
- 5. 缓存
- 总结与最佳实践
什么是 Webpack?
Webpack 是一个现代 JavaScript 应用程序的静态模块打包器。

当你告诉 Webpack 如何转换你的文件时,它会构建一个依赖图,该图映射了项目所需的每个模块,并生成一个或多个bundle。
Webpack 就像一个超级智能的打包工具,它能:
- 理解你的项目结构。
- 识别不同类型的文件(JS, CSS, 图片, 字体等)。
- 转换这些文件,使其能在浏览器中运行(将 ES6+ 转换成 ES5,将 Sass 转换成 CSS)。
- 合并所有转换后的文件,打包成少量(甚至一个)优化的文件,以提高网页加载速度。
为什么需要 Webpack?
在没有构建工具的时代,我们需要手动管理:
- 依赖关系:
<script>标签的顺序很重要,必须先加载依赖库,再加载使用它的代码。 - 兼容性:浏览器不能直接运行所有现代 JavaScript 特性(如 ES6, ES7)。
- 资源管理:CSS、图片等资源需要通过特定的方式引入到 HTML 中。
Webpack 解决了这些问题:

- 模块化:让你可以在任何类型的文件中使用
import/require语法来引入模块,而不仅仅是 JavaScript。 - 自动化:通过 Loader 和 Plugin,自动处理代码转换、压缩、优化等繁琐工作。
- 性能优化:提供代码分割、懒加载、缓存等策略,优化应用加载性能。
核心概念
在配置 Webpack 之前,必须理解以下几个核心概念:
-
Entry (入口)
- 指示 Webpack 应该使用哪个模块作为其构建内部依赖图的开始。
- Webpack 会从这个入口文件开始,递归地找出所有依赖的模块。
-
Output (出口)
- 告诉 Webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件。
- 默认情况下,主输出文件会放在
dist/main.js路径下。
-
Loader (加载器)
(图片来源网络,侵删)- Webpack 只能理解 JavaScript 和 JSON 文件,Loader 让 Webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用。
- 规则:在
webpack.config.js中,module.rules数组定义了一组规则,每个规则都包含一个test属性(用于匹配文件)和一个use属性(指定要使用的 loader)。
-
Plugin (插件)
- Loader 用于转换特定类型的文件,而 Plugin 则可以用于执行更广泛的任务,如打包优化、资源管理、环境变量注入等。
- 作用:通过在
webpack.config.js的plugins数组中实例化并添加插件来使用它们。
-
Mode (模式)
- 通过选择
development,production或none之中的一个,来设置mode参数,你可以启用 Webpack 内置在相应环境下的优化。 production模式会自动压缩代码、优化等。development模式会提供更快的构建速度和更详细的调试信息。
- 通过选择
第一个 Webpack 项目:零配置
从 Webpack 5 开始,它提供了一个零配置的体验,我们先用这个快速感受一下。
-
初始化项目
mkdir webpack-demo cd webpack-demo npm init -y
-
安装 Webpack
npm install webpack webpack-cli --save-dev
-
创建项目结构
webpack-demo/ ├── dist/ ├── src/ │ └── index.js └── package.json -
编写入口文件
src/index.jsfunction component() { const element = document.createElement('div'); const btn = document.createElement('button'); btn.innerHTML = 'Click me and check the console!'; element.appendChild(btn); btn.onclick = () => import(/* webpackChunkName: "print" */ './print.js').then(module => { const print = module.default; print(); }); return element; } document.body.appendChild(component()); -
创建被异步加载的文件
src/print.jsexport default function printMe() { console.log('I get called from print.js!'); } -
运行打包 在
package.json中添加脚本:"scripts": { "build": "webpack" }然后运行:
npm run build
你会看到
dist目录下生成了main.js文件,Webpack 自动配置了入口为src/index.js,并处理了动态导入。
手动配置 Webpack (核心步骤)
零配置虽然方便,但实际项目中我们通常需要手动配置来满足特定需求。
1. 项目初始化
mkdir my-webpack-project cd my-webpack-project npm init -y
2. 安装 Webpack
npm install webpack webpack-cli --save-dev
3. 创建 src 目录和入口文件
my-webpack-project/
├── dist/ # 存放打包后的文件
├── src/
│ ├── index.js # 入口文件
│ └── style.css # 样式文件
├── index.html # HTML 模板
└── package.json
src/index.js:
import _ from 'lodash';
import './style.css';
function createDomElement() {
const element = document.createElement('div');
element.innerHTML = _.join(['Hello', 'Webpack', 'with', 'Style!'], ' ');
return element;
}
document.body.appendChild(createDomElement());
src/style.css:
body {
background-color: #f0f0f0;
font-family: Arial, sans-serif;
}
div {
color: #333;
font-size: 24px;
text-align: center;
padding: 20px;
}
index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8">Webpack Demo</title> </head> <body> <!-- 引用打包后的 JS 文件 --> <script src="dist/main.js"></script> </body> </html>
4. 配置 webpack.config.js
在项目根目录下创建 webpack.config.js 文件。
const path = require('path');
module.exports = {
// 1. 入口
entry: './src/index.js',
// 2. 出口
output: {
// [name] 是一个占位符,会根据 entry 的 key 生成
filename: '[name].bundle.js',
// path 必须是绝对路径
path: path.resolve(__dirname, 'dist'),
// 每次构建前清空 dist 目录
clean: true,
},
// 3. 模块规则
module: {
rules: [
{
test: /\.css$/i, // 匹配 .css 文件
use: ['style-loader', 'css-loader'], // 从右到左使用 loader
},
],
},
// 4. 插件
plugins: [],
// 5. 模式
mode: 'development', // 可以是 'development' 或 'production'
};
解释:
path: Node.js 内置模块,用于处理文件路径。rules: 我们添加了一条规则,当遇到.css文件时,先使用css-loader解析 CSS 文件,然后使用style-loader将解析后的 CSS 内容通过<style>标签注入到 HTML 中。- 安装 Loaders: 我们需要安装这两个 loader。
npm install --save-dev style-loader css-loader
5. 配置打包脚本
修改 package.json 的 scripts 部分:
"scripts": {
"build": "webpack --config webpack.config.js"
}
--config 参数指定了配置文件的路径,如果你的配置文件名就是 webpack.config.js,可以省略此参数。
6. 运行打包
npm run build
dist 目录下会生成 main.bundle.js,在浏览器中打开 index.html,你应该能看到应用运行了,并且样式也被正确应用。
处理样式文件
1. 加载 CSS
我们已经在上一步中使用了 style-loader 和 css-loader,它们的作用是:
css-loader: 使你能够使用import或require导入 CSS 文件。style-loader: 将 CSS 插入到 DOM 的<style>标签中。
2. 提取 CSS 到单独文件
在生产环境中,我们通常希望将 CSS 提取到一个单独的文件中,而不是内联到 HTML 中,这样可以利用浏览器缓存,这时我们需要使用 mini-css-extract-plugin。
-
安装插件和 loader
npm install --save-dev mini-css-extract-plugin
-
修改
webpack.config.jsconst path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { // ... entry, output ... module: { rules: [ { test: /\.css$/i, // 生产环境使用 MiniCssExtractPlugin.loader,开发环境使用 style-loader use: [process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader', 'css-loader'], }, ], }, plugins: [ new MiniCssExtractPlugin({ // 提取出的 CSS 文件名 filename: '[name].css', }), ], // ... mode ... }; -
修改
index.html需要手动引入提取出的 CSS 文件。<link rel="stylesheet" href="dist/main.css"> <script src="dist/main.bundle.js"></script>
-
运行打包 运行
npm run build,你会看到dist目录下同时生成了main.bundle.js和main.css。
处理图片和其他文件
为了处理图片、字体等文件,我们需要 file-loader 或 url-loader(Webpack 5 推荐使用内置的 Asset Modules)。
使用 Asset Modules (Webpack 5 推荐)
-
修改
webpack.config.jsmodule.exports = { // ... module: { rules: [ // ... css rule ... { test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset/resource', // 生成单独的文件,并导出其 URL }, { test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource', }, ], }, // ... }; -
在 CSS 中使用图片
body { background-image: url('./assets/background.png'); background-size: cover; } -
创建
src/assets目录并放入一张background.png图片。 -
运行打包 Webpack 会自动将图片复制到
dist目录,并在 CSS 中更新其引用路径。
使用 Babel 转换 JavaScript
为了让你的现代 JavaScript 代码能在旧浏览器上运行,你需要使用 Babel。
-
安装 Babel 相关依赖
npm install --save-dev babel-loader @babel/core @babel/preset-env
babel-loader: Webpack 和 Babel 的桥梁。@babel/core: Babel 核心库。@babel/preset-env: 一个智能预设,根据你目标环境,决定转换哪些 JavaScript 特性。
-
创建 Babel 配置文件 在项目根目录创建
.babelrc文件:{ "presets": ["@babel/preset-env"] } -
修改
webpack.config.jsmodule.exports = { // ... module: { rules: [ // ... css and image rules ... { test: /\.m?js$/, // 匹配 .js 和 .mjs 文件 exclude: /node_modules/, // 排除 node_modules 目录 use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } } ] } // ... }; -
编写现代 JS 代码 在
src/index.js中使用一些 ES6+ 语法,如箭头函数、let/const等,然后运行打包,Babel 会将其转换为 ES5。
使用 Source Maps
当打包后的代码出错时,Source Maps 能帮助你在源代码中定位错误,而不是在压缩后的代码中。
在 webpack.config.js 中配置 devtool。
module.exports = {
// ...
devtool: 'inline-source-map', // 开发环境推荐
// 生产环境推荐 'source-map'
// ...
};
inline-source-map: Source Map 作为 Data URI 嵌入在 bundle 中。source-map: 生成一个单独的.map文件,性能更好,适合生产环境。
开发服务器
每次修改代码后都手动运行 npm run build 非常繁琐。webpack-dev-server 可以启动一个简单的 Web 服务器,并提供实时重载功能。
-
安装
npm install --save-dev webpack-dev-server
-
修改
webpack.config.js指定静态文件的根目录。module.exports = { // ... devServer: { static: './dist', // 静态文件服务的目录 }, // ... }; -
修改
package.json脚本"scripts": { "build": "webpack", "start": "webpack serve --open" }--open选项会自动在浏览器中打开页面。 -
启动开发服务器
npm start
现在当你修改代码并保存时,浏览器会自动刷新。
高级优化
1. 生产环境与开发环境分离
最佳实践是为开发和生产环境创建不同的配置文件。
-
创建配置文件
webpack.common.js(公共配置)webpack.dev.js(开发配置)webpack.prod.js(生产配置)
-
安装
webpack-mergenpm install --save-dev webpack-merge
-
编写配置文件
-
webpack.common.js:const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { entry: './src/index.js', output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist'), clean: true, }, module: { rules: [ { test: /\.css$/i, use: [MiniCssExtractPlugin.loader, 'css-loader'], }, // ... other rules ... ], }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', }), ], }; -
webpack.dev.js:const { merge } = require('webpack-merge'); const common = require('./webpack.common.js'); module.exports = merge(common, { mode: 'development', devtool: 'inline-source-map', devServer: { static: './dist', }, }); -
webpack.prod.js:const { merge } = require('webpack-merge'); const common = require('./webpack.common.js'); module.exports = merge(common, { mode: 'production', devtool: 'source-map', // 生产环境可以添加更多插件,如 TerserPlugin (Webpack 已内置) });
-
-
修改
package.json脚本"scripts": { "build": "webpack --config webpack.prod.js", "start": "webpack serve --config webpack.dev.js --open" }
2. 代码分割
代码分割允许你将代码拆分成多个 bundle,然后可以按需加载或并行加载,这可以通过以下几种方式实现:
- 入口起点: 使用
entry配置手动地分离代码。 - 防止重复: 使用
SplitChunksPlugin去重和分离 chunk。 - 动态导入: 通过内联函数调用的方式分离代码。
动态导入 是最推荐的方式,因为它能实现按需加载。
我们已经在前面的 src/index.js 中使用了动态导入:
btn.onclick = () => import(/* webpackChunkName: "print" */ './print.js').then(module => {
const print = module.default;
print();
});
/* webpackChunkName: "print" */ 是一个魔法注释,告诉 Webpack 将这个 chunk 命名为 print.js,运行 npm run build,你会看到 dist 目录下除了 main 相关文件,还有一个 print.[hash].js 文件。
3. 懒加载
代码分割是实现懒加载的基础,当用户需要时(如点击按钮、滚动到某个位置)才加载对应的代码块,从而减小初始包的大小。
上面的动态导入示例就是懒加载的典型应用。
4. Tree Shaking (摇树优化)
Tree Shaking 指的是移除 JavaScript 上下文中的未引用代码(Dead Code),它依赖于 ES2025 模块语法的静态结构特性(import 和 export)。
如何启用:
- 使用 ES6 模块语法 (
import/export)。 - 在
package.json中设置"sideEffects": false,或者明确指定哪些文件有副作用。 - 确保
mode是production。
Webpack 在生产模式下会自动开启 Tree Shaking。
5. 缓存
为了利用浏览器缓存,我们需要确保文件名在内容不变时保持不变,内容变化时文件名变化。
Webpack 的 [contenthash] 占位符可以完美解决这个问题。
修改 webpack.common.js 中的 output.filename:
output: {
filename: '[name].[contenthash].bundle.js', // 使用 contenthash
// ...
},
只有当文件内容发生变化时,[contenthash] 才会改变。
总结与最佳实践
- 从零开始: 先理解 Entry, Output, Loader, Plugin 这四大核心概念。
- 分步配置: 不要试图一次性配置所有功能,先配置 JS 打包,然后是 CSS,接着是图片和字体,最后是优化。
- 环境分离: 始终为开发和生产环境创建不同的配置文件,使用
webpack-merge合并公共配置。 - 利用工具: Webpack 5 的零配置、Asset Modules 等新特性极大地简化了配置。
- 持续学习: Webpack 生态非常庞大,还有许多高级主题如 PWA、环境变量注入等,可以在需要时再深入学习。
这份教程涵盖了 Webpack 的绝大部分核心功能和最佳实践,掌握了这些,你就可以应对绝大多数前端项目的构建需求了,祝你学习愉快!
