webpack 的配置项看似繁多,但本质上都围绕五个核心概念展开:entry(入口)、output(输出)、loader(加载器)、plugin(插件)、mode(模式)。理解了这五个概念,就等于掌握了 webpack 配置的主干。本文逐一讲解它们的职责、常用写法以及彼此之间的关系。
一、Entry:入口
入口(entry) 是 webpack 构建依赖图的起点。webpack 会从入口文件出发,递归地找出所有直接或间接依赖的模块,最终构建出完整的依赖关系图。可以说,没有入口,webpack 就不知道从哪里开始打包。
最简单的单入口写法:
module.exports = {
entry: './src/index.js',
};
entry 也支持对象语法,用于多入口场景:
module.exports = {
entry: {
app: './src/app.js',
admin: './src/admin.js',
},
};
多入口常用于多页面应用(MPA),每个入口会生成一份独立的依赖图。此外,entry 还支持数组语法,把多个模块合并打包成一个入口:
module.exports = {
entry: ['./src/polyfills.js', './src/index.js'],
};
小贴士:
context配置项可以指定 entry 的解析基准目录,默认是 webpack 启动时的工作目录。
二、Output:输出
输出(output) 决定打包产物写到哪里、叫什么名字。入口定义了「从哪来」,output 定义了「到哪去」。
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
clean: true,
},
};
几个常用字段:
- path:产物输出的绝对路径,通常是
path.resolve(__dirname, 'dist')。注意它必须是绝对路径。 - filename:输出文件名。多入口时可用占位符区分,例如
[name].bundle.js,其中[name]对应 entry 的 key。 - clean:每次构建前自动清理 output 目录,避免残留旧文件。
- publicPath:指定产物在浏览器中引用的公共路径,常用于 CDN 或静态资源分离场景。
配合多入口输出:
module.exports = {
entry: {
app: './src/app.js',
admin: './src/admin.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true,
},
};
其中 [contenthash] 会随文件内容变化,是浏览器长久缓存的常用手段。
三、Loader:加载器
webpack 本身只理解 JavaScript 和 JSON。Loader 的作用就是把其他类型的文件「翻译」成 webpack 能处理的模块——CSS、图片、字体、TypeScript 等都靠它。
Loader 配置在 module.rules 中,通过 test 匹配文件,通过 use 指定处理链:
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset/resource',
},
],
},
};
理解 loader 最关键的一点是执行顺序:use 数组中的 loader **从右到左(从后到前)**依次执行。以上面的 CSS 为例,实际是先用 css-loader 解析 CSS 文件中的 @import 和 url(),再用 style-loader 把样式注入到页面中。
常用 loader 举例:
| Loader | 作用 |
|---|---|
babel-loader | 把 ES6+ 语法转译为向后兼容的 JS |
css-loader / style-loader | 解析 CSS、注入样式 |
sass-loader / less-loader | 编译 Sass / Less |
ts-loader | 编译 TypeScript |
file-loader / url-loader | 处理文件资源(webpack 5 推荐用 asset modules) |
每个 loader 还可以通过对象语法传入 option:
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
}
四、Plugin:插件
如果说 loader 负责「转换模块」,那么 Plugin 负责的是「扩展 webpack 本身的构建能力」。它可以在构建生命周期的各个阶段挂载钩子,做 loader 做不到的事情,比如生成 HTML、抽离 CSS、压缩代码、定义全局变量、分析打包体积等。
使用插件一般分两步:require 引入,然后在 plugins 数组中 new 一个实例:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { DefinePlugin } = require('webpack');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
new DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
],
};
常用插件举例:
| Plugin | 作用 |
|---|---|
HtmlWebpackPlugin | 自动生成 HTML 并注入打包产物 |
MiniCssExtractPlugin | 把 CSS 抽离成独立文件 |
CopyWebpackPlugin | 拷贝静态资源 |
DefinePlugin | 定义编译期全局常量 |
BundleAnalyzerPlugin | 可视化分析打包体积 |
Loader 与 Plugin 的区别:loader 面向「文件/模块」,是一个转换函数,运行在模块加载阶段;plugin 面向「整个构建流程」,基于钩子机制,能力更强、范围更广。
五、Mode:模式
Mode 用于告诉 webpack 当前是开发还是生产环境,从而自动启用对应的内置优化:
module.exports = {
mode: 'development', // 或 'production' / 'none'
};
三种取值:
- development:开启便于调试的能力,如更快的构建、可读的模块名、开发期 Source Map,不做代码压缩。
- production(默认):开启 Tree Shaking、代码压缩、作用域提升(Scope Hoisting)等优化,产物更小。
- none:不启用任何默认优化,所有行为完全由配置决定。
也可以通过命令行或 npm scripts 传入:
{
"scripts": {
"dev": "webpack serve --mode development",
"build": "webpack --mode production"
}
}
从 webpack 4 起,mode 是必填项(未显式指定时会给出警告)。合理使用 mode 能省去大量手工优化配置。
五大概念一览
| 概念 | 职责 | 关键配置 |
|---|---|---|
| Entry | 构建依赖图的起点 | entry |
| Output | 决定产物的位置与命名 | output |
| Loader | 把非 JS/JSON 资源转换为模块 | module.rules |
| Plugin | 扩展构建流程、执行更复杂的任务 | plugins |
| Mode | 启用对应环境的内置优化 | mode |
小结
一个完整的 webpack 配置,基本就是这五大概念的组合:从 entry 进入,经 loader 转换各类资源,由 plugin 扩展构建能力,最终按 output 的规则输出,而 mode 则决定整体优化的档位。把这五点记牢,再复杂的配置也能拆解成清晰的结构。
此外,webpack 还有 module(模块)、chunk(代码块)、bundle(产物包) 等延伸概念,它们描述的是打包过程中的粒度与产物形态,后续可以单独展开。