Skip to content
On this page

Webpack 开发环境配置

开发环境配置是 Webpack 设置的重要组成部分,它直接影响开发体验、构建速度和调试效率。本章将详细介绍如何配置适合开发环境的 Webpack。

开发环境基础配置

基础开发配置

javascript
module.exports = {
  mode: 'development',                       // 开发模式
  devtool: 'eval-source-map',               // 生成源映射,便于调试
  entry: './src/index.js',                  // 入口文件
  output: {
    filename: '[name].js',                  // 不使用哈希,便于调试
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'                         // 开发服务器公共路径
  }
};

开发服务器配置

javascript
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  devServer: {
    static: {                              // 静态文件服务
      directory: path.join(__dirname, 'public'),
      watch: true                          // 监听文件变化
    },
    compress: true,                        // 启用 gzip 压缩
    port: 3000,                           // 端口号
    open: true,                           // 自动打开浏览器
    hot: true,                            // 启用热模块替换
    historyApiFallback: true,              // 支持 HTML5 History API
    client: {
      overlay: true,                       // 显示编译错误
      progress: true                       // 显示编译进度
    }
  }
};

源映射配置

开发环境源映射选项

javascript
module.exports = {
  mode: 'development',
  devtool: 'eval-source-map',              // 最佳调试体验,但构建较慢
  // devtool: 'eval',                      // 构建最快,但调试体验一般
  // devtool: 'eval-cheap-source-map',     // 平衡构建速度和调试体验
  // devtool: 'eval-cheap-module-source-map', // 推荐用于开发
  entry: './src/index.js'
};

// 不同 devtool 选项的对比:
// 'eval' - 构建最快,但无法显示原始代码
// 'eval-source-map' - 调试最佳,但构建最慢
// 'eval-cheap-module-source-map' - 推荐开发使用
// 'eval-cheap-source-map' - 较快的构建和较好的调试

生产环境源映射配置

javascript
module.exports = {
  mode: 'production',
  devtool: 'source-map',                   // 生产环境使用完整源映射
  // devtool: 'hidden-source-map',         // 不在浏览器中显示源映射
  // devtool: 'nosources-source-map',      // 不包含源代码
  entry: './src/index.js'
};

热模块替换(HMR)

基础 HMR 配置

javascript
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  devServer: {
    hot: true                              // 启用热模块替换
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()  // HMR 插件
  ]
};

React HMR 配置

javascript
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  devServer: {
    hot: true
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-react'],
            plugins: [
              'react-refresh/babel'        // React Fast Refresh
            ]
          }
        },
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new ReactRefreshWebpackPlugin()        // React Fast Refresh 插件
  ]
};

Vue HMR 配置

javascript
module.exports = {
  mode: 'development',
  entry: './src/main.js',
  devServer: {
    hot: true
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: 'vue-loader'
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
};

开发服务器高级配置

代理配置

javascript
module.exports = {
  mode: 'development',
  devServer: {
    static: './dist',
    port: 3000,
    proxy: {                             // 代理配置
      '/api': {
        target: 'http://localhost:8080', // 目标服务器
        changeOrigin: true,              // 改变源
        secure: false,                   // 不验证 SSL 证书
        pathRewrite: {                   // 路径重写
          '^/api': '/api/v1'
        },
        onProxyReq: (proxyReq, req, res) => {
          // 代理请求时的回调
          console.log('Proxying request:', req.url);
        }
      },
      // 多个代理配置
      '/auth': {
        target: 'http://auth-server:3001',
        changeOrigin: true
      }
    }
  }
};

HTTPS 配置

javascript
module.exports = {
  devServer: {
    static: './dist',
    https: true,                         // 启用 HTTPS
    // 或者自定义证书
    https: {
      key: fs.readFileSync('/path/to/server.key'),
      cert: fs.readFileSync('/path/to/server.crt'),
      ca: fs.readFileSync('/path/to/ca.pem')
    },
    port: 3000
  }
};

多页面应用配置

javascript
module.exports = {
  mode: 'development',
  entry: {
    home: './src/pages/home/index.js',
    about: './src/pages/about/index.js',
    contact: './src/pages/contact/index.js'
  },
  devServer: {
    static: {
      directory: path.join(__dirname, 'public')
    },
    port: 3000,
    historyApiFallback: {
      // 多页面应用的路由回退
      rewrites: [
        { from: /^\/$/, to: '/home.html' },
        { from: /^\/home/, to: '/home.html' },
        { from: /^\/about/, to: '/about.html' },
        { from: /^\/contact/, to: '/contact.html' }
      ]
    }
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'
  }
};

## 性能优化配置

### 开发环境性能优化

```javascript
module.exports = {
  mode: 'development',
  // 启用缓存以加快重建速度
  cache: {
    type: 'filesystem',                   // 文件系统缓存
    buildDependencies: {
      config: [__filename]                // 配置文件变更时清除缓存
    }
  },
  
  // 优化构建性能
  optimization: {
    moduleIds: 'named',                   // 使用命名模块 ID,便于调试
    chunkIds: 'named',                    // 使用命名块 ID
    emitOnErrors: false,                  // 构建错误时不输出资源
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        // 开发环境可能不需要复杂的代码分割
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,
          chunks: 'all'
        }
      }
    }
  },
  
  // 减少监听文件的数量
  snapshot: {
    managedPaths: [                       // Webpack 监控的路径
      path.resolve(__dirname, 'node_modules')
    ],
    immutablePaths: [                     // 不变路径
      path.resolve(__dirname, 'immutable-deps')
    ]
  }
};

模块解析优化

javascript
module.exports = {
  resolve: {
    // 优化模块解析
    modules: [
      path.resolve(__dirname, 'src'),     // 优先查找项目源码
      'node_modules'                      // 然后查找 node_modules
    ],
    
    // 减少扩展名尝试
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
    
    // 使用别名减少解析时间
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils')
    },
    
    // 解析时的主字段
    mainFields: ['browser', 'module', 'main']
  },
  
  // 排除不需要解析的模块
  module: {
    noParse: [
      /jquery/,                          // 不解析 jQuery
      /lodash/                           // 不解析 Lodash
    ]
  }
};

开发工具集成

ESLint 集成

javascript
module.exports = {
  mode: 'development',
  module: {
    rules: [
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true        // 启用 Babel 缓存
            }
          },
          {
            loader: 'eslint-loader',
            options: {
              fix: true,                  // 自动修复
              emitWarning: true,          // 以警告形式显示错误
              configFile: './.eslintrc.js'
            }
          }
        ]
      }
    ]
  }
};

TypeScript 开发配置

javascript
module.exports = {
  mode: 'development',
  entry: './src/index.tsx',
  devtool: 'eval-source-map',
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx']
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: [
          {
            loader: 'ts-loader',
            options: {
              transpileOnly: true,        // 只转译,不进行类型检查
              happyPackMode: true         // 启用 happypack 模式
            }
          }
        ],
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    // 使用 ForkTsCheckerWebpackPlugin 进行类型检查
    new ForkTsCheckerWebpackPlugin({
      typescript: {
        diagnosticOptions: {
          semantic: true,
          syntactic: true
        }
      }
    })
  ]
};

环境变量配置

开发环境变量

javascript
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('development'),
      'process.env.API_URL': JSON.stringify('http://localhost:8080'),
      'process.env.DEBUG': JSON.stringify(true),
      '__DEV__': JSON.stringify(true),
      '__PRODUCTION__': JSON.stringify(false)
    })
  ]
};

使用 dotenv 配置环境变量

javascript
const webpack = require('webpack');
const dotenv = require('dotenv');

// 加载 .env 文件
const env = dotenv.config().parsed;

// 将 .env 中的变量转换为 webpack 需要的格式
const envKeys = Object.keys(env).reduce((prev, next) => {
  prev[`process.env.${next}`] = JSON.stringify(env[next]);
  return prev;
}, {});

module.exports = {
  mode: 'development',
  plugins: [
    new webpack.DefinePlugin(envKeys)
  ]
};

开发环境完整配置示例

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');

module.exports = {
  mode: 'development',
  devtool: 'eval-cheap-module-source-map',
  
  entry: {
    app: [
      'react-hot-loader/patch',          // React 热加载补丁
      'webpack-dev-server/client?http://localhost:3000',
      'webpack/hot/only-dev-server',
      './src/index.js'
    ]
  },
  
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/',
    hotUpdateChunkFilename: 'hot/hot-update.js',
    hotUpdateMainFilename: 'hot/hot-update.json'
  },
  
  devServer: {
    contentBase: path.join(__dirname, 'public'),
    hot: true,
    port: 3000,
    open: true,
    historyApiFallback: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    },
    clientLogLevel: 'none',
    overlay: {
      warnings: false,
      errors: true
    }
  },
  
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: [
                ['@babel/preset-env', {
                  targets: {
                    browsers: ['last 2 versions']
                  }
                }],
                '@babel/preset-react'
              ],
              plugins: [
                'react-refresh/babel'
              ],
              cacheDirectory: true
            }
          }
        ],
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[local]_[hash:base64:5]'
              },
              sourceMap: true
            }
          }
        ]
      },
      {
        test: /\.(png|jpg|gif|svg)$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[hash:7].[ext]',
              outputPath: 'images/'
            }
          }
        ]
      }
    ]
  },
  
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new ReactRefreshWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './public/index.html',
      filename: 'index.html'
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('development')
    })
  ],
  
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils')
    },
    extensions: ['.js', '.jsx', '.json']
  },
  
  optimization: {
    moduleIds: 'named',
    chunkIds: 'named'
  }
};

小结

开发环境的配置重点在于提高开发体验和构建速度。关键配置包括启用热模块替换、使用合适的源映射、配置开发服务器、优化构建性能等。合理的开发环境配置能够显著提升开发效率。