人人都会AI编程

17.2 Webpack 构建 React 项目

更新时间:2026-07-10

虽然在新的项目中 Vite 已经成为主流选择,但 Webpack 仍然是大量现有企业级项目的构建基础。理解 Webpack 如何搭建 React 项目,能让你具备维护老项目、优化打包策略的能力。本节将以实用为导向,介绍从零搭建一套可用的 Webpack + React 构建配置。

核心依赖与角色

一个基本的 Webpack React 项目需要以下依赖:

  • webpackwebpack-cli:核心打包工具与命令行接口
  • webpack-dev-server:开发环境热更新服务器
  • babel-loader + @babel/core:将现代 JS/JSX 语法转换为兼容版本
  • @babel/preset-env:按目标浏览器转换 ES6+ 语法
  • @babel/preset-react:处理 JSX 语法
  • html-webpack-plugin:自动生成 HTML 文件并注入打包产物
  • css-loaderstyle-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 提供三种主要的代码分割方式:

  1. 多入口配置:手动拆分独立页面
  2. SplitChunksPlugin:自动提取公共依赖(如 React、ReactDOM)为独立 chunk
  3. 动态导入:配合 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 在生产模式下会自动开启。但需要确保以下两点:

  1. 使用 ES Module:代码中统一使用 import/export 语法,避免 require/module.exports
  2. 标记副作用:在 package.json 中设置 "sideEffects": false 或指定有副作用的文件列表,告诉 Webpack 哪些模块可以安全移除

例如,组件库通常会在 package.json 中标识 CSS 文件有副作用:

{
  "sideEffects": ["*.css"]
}

打包体积优化

  1. 压缩 JS:使用 terser-webpack-plugin(生产模式默认启用,可自定义配置)
  2. 压缩 CSS:使用 css-minimizer-webpack-plugin
  3. 分析体积:通过 webpack-bundle-analyzer 可视化分析 chunk 构成,定位大库
  4. 图片优化:对于小型图片,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 工程构建任务,但同时也要求开发者花时间理解配置细节。掌握上述核心能力后,处理大多数业务场景已经足够。