虽然在新的项目中 Vite 已经成为主流选择,但 Webpack 仍然是大量现有企业级项目的构建基础。理解 Webpack 如何搭建 React 项目,能让你具备维护老项目、优化打包策略的能力。本节将以实用为导向,介绍从零搭建一套可用的 Webpack + React 构建配置。
核心依赖与角色
一个基本的 Webpack React 项目需要以下依赖:
- webpack、webpack-cli:核心打包工具与命令行接口
- webpack-dev-server:开发环境热更新服务器
- babel-loader + @babel/core:将现代 JS/JSX 语法转换为兼容版本
- @babel/preset-env:按目标浏览器转换 ES6+ 语法
- @babel/preset-react:处理 JSX 语法
- html-webpack-plugin:自动生成 HTML 文件并注入打包产物
- css-loader、style-loader:处理 CSS 模块
生产环境通常还会加入 mini-css-extract-plugin(提取独立 CSS 文件)、terser-webpack-plugin(压缩 JS)、css-minimizer-webpack-plugin(压缩 CSS)等。
Babel 配置
Babel 的配置一般放在项目根目录的 babel.config.js 或 .babelrc 中:
module.exports = {
presets: [
['@babel/preset-env', { targets: '> 0.25%, not dead' }],
['@babel/preset-react', { runtime: 'automatic' }] // 自动引入 React,无需手动 import React
]
};
runtime: 'automatic' 对应 React 17+ 的新 JSX 转换,减少了打包体积。
基础 Loader 配置
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] // 开发模式直接注入 style 标签
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset' // webpack 5 内置资源模块,自动选择 base64 或文件输出
}
]
}
};
对于生产环境,需要将 CSS 提取到独立文件,而不是用 style-loader 注入:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [new MiniCssExtractPlugin({ filename: '[name].[contenthash].css' })]
};
使用 contenthash 可以在内容不变时复用缓存。
代码分割
Webpack 提供三种主要的代码分割方式:
- 多入口配置:手动拆分独立页面
- SplitChunksPlugin:自动提取公共依赖(如 React、ReactDOM)为独立 chunk
- 动态导入:配合 React.lazy 实现路由级/组件级懒加载
SplitChunks 配置示例:
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
这样 node_modules 中的第三方库会被打包到 vendors.[hash].js,与应用代码分离,利用浏览器缓存减少重复加载。
动态导入与 React.lazy:
const Profile = React.lazy(() => import('./Profile'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Profile />
</Suspense>
);
}
Webpack 检测到 import() 语法后会自动将其拆分为独立的 chunk,只在需要时加载。
Tree Shaking
Tree Shaking 依赖于 ES Module 静态结构,Webpack 在生产模式下会自动开启。但需要确保以下两点:
- 使用 ES Module:代码中统一使用
import/export语法,避免require/module.exports - 标记副作用:在
package.json中设置"sideEffects": false或指定有副作用的文件列表,告诉 Webpack 哪些模块可以安全移除
例如,组件库通常会在 package.json 中标识 CSS 文件有副作用:
{
"sideEffects": ["*.css"]
}
打包体积优化
- 压缩 JS:使用
terser-webpack-plugin(生产模式默认启用,可自定义配置) - 压缩 CSS:使用
css-minimizer-webpack-plugin - 分析体积:通过
webpack-bundle-analyzer可视化分析 chunk 构成,定位大库 - 图片优化:对于小型图片,
type: 'asset'会自动内联为 base64,减少请求数;大图可配合image-webpack-loader压缩
典型的优化配置段:
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({ extractComments: false }),
new CssMinimizerPlugin()
],
splitChunks: { ... } // 代码分割
}
环境分离与 webpack-merge
通常我们会维护三个配置文件:
webpack.common.js:公共配置(入口、输出、resolve 等)webpack.dev.js:开发配置(devServer、source map、热更新)webpack.prod.js:生产配置(压缩、提取 CSS、优化)
使用 webpack-merge 组合它们:
// webpack.dev.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
devServer: {
port: 3000,
hot: true,
historyApiFallback: true // 支持 BrowserRouter
}
});
生产模式则设置 mode: 'production',devtool: 'source-map'。
一个最小可用的 Webpack React 配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.jsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|gif)$/,
type: 'asset'
}
]
},
plugins: [
new HtmlWebpackPlugin({ template: './public/index.html' })
],
devServer: {
port: 3000,
hot: true,
historyApiFallback: true
}
};
真实项目中,这仅是一个起点。根据实际需求逐步增加 TypeScript、Sass、PostCSS 等 loader,并配置环境变量、代理、代码分割优化。Webpack 的灵活性让它可以胜任任何复杂度的 React 工程构建任务,但同时也要求开发者花时间理解配置细节。掌握上述核心能力后,处理大多数业务场景已经足够。