Webpack 5 配置完全指南:从入门到精通

Hi,我是前端人类学(之前叫布兰妮甜)!
Webpack 是现代前端开发中不可或缺的模块打包工具,它的强大功能和灵活性使其成为构建复杂前端应用的首选。本文将深入探讨 Webpack 5 的配置细节,带你从基础配置到高级优化,全面掌握 Webpack 5 的使用技巧。



一、环境准备与核心概念

1.1. 安装

  npm i -D webpack webpack-cli webpack-dev-server@latest

1.2 核心概念

  1. 入口(Entry):指定 webpack 从哪个模块开始构建依赖图
  2. 输出(Output):告诉 webpack 在哪里输出它创建的 bundle 文件
  3. 加载器(Loader):让 webpack 能够处理非 JavaScript 文件「翻译官」
  4. 插件(Plugins):执行范围更广的任务,从打包优化到资源管理「任务管家」
  5. 模式(Mode):可以设置为 development、production 或 none

二、基础配置

// webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'main.js',
    clean: true  // 5.20+ 自动清空 dist
  },
  mode: 'development'
};

这个配置告诉 webpack:

  • 从 src/index.js 开始打包
  • 输出到 dist/main.js
  • 使用开发模式(不压缩代码,有 source map 等)

三、入口/出口(entry/output)深度解析

  1. 多入口

    entry: {
      home: './src/home.js',
      about: './src/about.js',
    }
    
  2. 输出占位符 [name] [contenthash:8] [fullhash] [id]

    output: {
      filename: 'js/[name].[contenthash:8].js',
      assetModuleFilename: 'assets/[name].[hash][ext]', // 图片/字体统一路径
    }
    

四、处理不同类型的资源

4.1 JS/TS

npm i -D babel-loader @babel/core @babel/preset-env core-js@3
{
  test: /\.[jt]sx?$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: [
        ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }],
        '@babel/preset-typescript',
        ['@babel/preset-react', { runtime: 'automatic' }],
      ],
    },
  },
},

4.2 CSS/LESS/Sass

npm i -D css-loader mini-css-extract-plugin postcss-loader autoprefixer cssnano
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
{
  test: /\.s?css$/,
  use: [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: ['autoprefixer', ...(process.env.NODE_ENV === 'production' ? ['cssnano'] : [])],
        },
      },
    },
    'sass-loader',
  ],
},
plugins: [
  new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash:8].css' }),
],

这里我们使用了四个 loader:

  • css-loader
    • 作用:解析 CSS 文件中的 @importurl() 等引用
    • 功能:处理 CSS 模块之间的依赖关系,将 CSS 转换为 JavaScript 模块
  • postcss-loader
    • 作用:通过 PostCSS 处理 CSS 代码
    • 功能:
      • 使用 autoprefixer 插件为 CSS 属性添加浏览器厂商前缀
      • 在生产环境(NODE_ENV === 'production')下使用 cssnano 插件压缩和优化 CSS
  • MiniCssExtractPlugin.loader
    • 作用:将 CSS 从 JavaScript bundle 中提取到单独的 CSS 文件
    • 功能:替代了 style-loader,用于生产环境优化
  • sass-loader
    • 作用:将 Sass/SCSS 文件编译为 CSS
    • 功能:支持使用 Sass 语法编写样式文件

这四个 loader 共同构成了完整的 CSS 处理链,从 Sass 编译到 CSS 提取和优化。

4.3 图片/字体/资源

module.exports = {
  // ...其他配置
  module: {
    rules: [
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
    ],
  },
};

Webpack 5 引入了资源模块(asset modules),可以替代 file-loader 和 url-loader。

五、使用插件增强功能

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  // ...其他配置
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'My App',
      template: './src/index.html'
    }),
  ],
};
  • CleanWebpackPlugin:在每次构建前清理输出目录
  • HtmlWebpackPlugin:自动生成 HTML 文件并注入打包后的资源

六、开发环境配置

为了提高开发体验,我们需要配置开发服务器和 source map:

module.exports = {
  // ...其他配置
  devtool: 'inline-source-map',
  devServer: {
    contentBase: './dist',
    hot: true,
  },
};

然后安装 webpack-dev-server:

npm install --save-dev webpack-dev-server

在 package.json 中添加脚本:

"scripts": {
  "start": "webpack serve --open"
}

七、生产环境优化

const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
  mode: 'production',
  optimization: {
    minimizer: [
      new TerserPlugin(),
      new CssMinimizerPlugin(),
    ],
    splitChunks: {
      chunks: 'all',
    },
  },
  performance: {
    hints: false,
    maxEntrypointSize: 512000,
    maxAssetSize: 512000
  },
};
  • TerserPlugin:压缩 JavaScript
  • CssMinimizerPlugin:压缩 CSS
  • splitChunks:代码分割,提取公共依赖

八、高级配置技巧

8.1 环境变量配置

我们可以区分开发和生产环境:

const webpack = require('webpack');

module.exports = (env) => {
  return {
    // ...其他配置
    plugins: [
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(env.production ? 'production' : 'development')
      }),
    ],
  };
};

8.2 多页面应用配置

module.exports = {
  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/pageOne/index.html',
      filename: 'pageOne.html',
      chunks: ['pageOne']
    }),
    new HtmlWebpackPlugin({
      template: './src/pageTwo/index.html',
      filename: 'pageTwo.html',
      chunks: ['pageTwo']
    }),
  ],
};

8.3 自定义 loader 和插件

创建自定义 loader:

// my-loader.js
module.exports = function(source) {
  return source.replace('World', 'Webpack');
};

创建自定义插件:

// my-plugin.js
class MyPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('MyPlugin', (stats) => {
      console.log('编译完成!');
    });
  }
}

module.exports = MyPlugin;

九、Webpack 5 新特性

  1. 持久化缓存:显著提高构建速度

     module.exports = {
      cache: {
        type: 'filesystem',
      },
    };
    
  2. 资源模块:内置处理资源文件的能力,不再需要 file-loader 等

  3. 模块联邦:实现微前端架构

     // 作为远程模块
    new ModuleFederationPlugin({
      name: 'app1',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/Button',
      },
    });
    
    // 作为主机使用远程模块
    new ModuleFederationPlugin({
      name: 'app2',
      remotes: {
        app1: 'app1@http://localhost:3001/remoteEntry.js',
      },
    });
    
  4. Tree Shaking 改进:更好地消除未使用代码

  5. 长期缓存改进:通过确定的 chunk、模块 ID 和导出名称实现

十、性能优化

10.1 构建速度优化

module.exports = {
  // ...其他配置
  resolve: {
    extensions: ['.js', '.jsx', '.json'],
    alias: {
      '@': path.resolve(__dirname, 'src/'),
    },
    modules: [path.resolve(__dirname, 'src'), 'node_modules'],
  },
};

10.2 输出文件优化

module.exports = {
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
  },
};

使用 contenthash 可以实现长期缓存,只有文件内容变化时 hash 才会改变。

10.3 代码分割

module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    },
    runtimeChunk: 'single',
  },
};

十一、常见问题解决方案

  1. 处理路径问题

     module.exports = {
      resolve: {
        alias: {
          Assets: path.resolve(__dirname, 'src/assets/')
        }
      }
    };
    
  2. 处理 polyfill
    Webpack 5 不再自动包含 Node.js 核心模块的 polyfill,需要手动添加:

     module.exports = {
      resolve: {
        fallback: {
          "crypto": require.resolve("crypto-browserify"),
          "stream": require.resolve("stream-browserify"),
          "assert": require.resolve("assert"),
          "http": require.resolve("stream-http"),
          "https": require.resolve("https-browserify"),
          "os": require.resolve("os-browserify"),
          "url": require.resolve("url"),
          "buffer": require.resolve("buffer"),
        }
      }
    };
    
  3. 处理大型项目构建慢的问题

    • 使用 thread-loader 并行处理
    • 使用 cache-loader 缓存 loader 结果
    • 使用 DLLPlugin 预编译不常变化的模块

十二、完整配置示例

最后,我们来看一个完整的 webpack 配置示例:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = (env) => {
  const isProduction = env.production;

  return {
    entry: './src/index.js',
    output: {
      filename: isProduction ? '[name].[contenthash].js' : '[name].js',
      path: path.resolve(__dirname, 'dist'),
      publicPath: '/',
    },
    mode: isProduction ? 'production' : 'development',
    devtool: isProduction ? 'source-map' : 'eval-cheap-module-source-map',
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          use: 'babel-loader',
        },
        {
          test: /\.css$/i,
          use: [
            isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
            'css-loader',
            'postcss-loader',
          ],
        },
        {
          test: /\.(png|svg|jpg|jpeg|gif)$/i,
          type: 'asset/resource',
          generator: {
            filename: 'images/[hash][ext][query]',
          },
        },
        {
          test: /\.(woff|woff2|eot|ttf|otf)$/i,
          type: 'asset/resource',
          generator: {
            filename: 'fonts/[hash][ext][query]',
          },
        },
      ],
    },
    plugins: [
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
        template: './src/index.html',
        minify: isProduction ? {
          collapseWhitespace: true,
          removeComments: true,
        } : false,
      }),
      isProduction && new MiniCssExtractPlugin({
        filename: '[name].[contenthash].css',
      }),
    ].filter(Boolean),
    optimization: {
      minimizer: [
        new TerserPlugin(),
        new CssMinimizerPlugin(),
      ],
      splitChunks: {
        chunks: 'all',
      },
      runtimeChunk: 'single',
    },
    devServer: {
      contentBase: './dist',
      hot: true,
      historyApiFallback: true,
    },
    resolve: {
      extensions: ['.js', '.jsx'],
      alias: {
        '@': path.resolve(__dirname, 'src/'),
      },
    },
    performance: {
      hints: isProduction ? 'warning' : false,
    },
    cache: {
      type: 'filesystem',
    },
  };
};

Webpack 5 的配置不是「越复杂越好」,而是「按需组合」。先跑通最小配置,再逐步叠加功能,配合持久化缓存和合理拆分,可以把冷启动时间从分钟级降到秒级。祝构建愉快!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

前端人类学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值