# 基础到高级的Webpack配置详解

# 目录

  1. Webpack基础概念
  2. 基础配置
  3. 加载器(Loader)配置
  4. 插件(Plugins)使用
  5. 优化构建性能
  6. 代码分割与懒加载
  7. 环境配置管理
  8. 模块热替换(HMR)
  9. 高级配置案例
  10. Webpack 5新特性
  11. 常见问题与解决方案

# Webpack基础概念

# 什么是Webpack?

Webpack是一个现代JavaScript应用程序的静态模块打包器。当Webpack处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle。

# 核心概念

  1. 入口(Entry): 指定webpack开始构建其内部依赖图的起点
  2. 输出(Output): 告诉webpack在哪里输出所创建的bundle
  3. 加载器(Loaders): 让webpack能够处理非JavaScript文件
  4. 插件(Plugins): 用于执行范围更广的任务,如打包优化、资源管理等
  5. 模式(Mode): 设置development, production或none来启用相应模式下的优化

# 安装与基本使用

# 安装webpack和webpack-cli
npm install webpack webpack-cli --save-dev

# 创建基本的配置文件
touch webpack.config.js
1
2
3
4
5

# 基础配置

# 最小配置示例

// webpack.config.js
const path = require('path');

module.exports = {
  // 入口文件
  entry: './src/index.js',
  
  // 输出配置
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  
  // 模式设置
  mode: 'development'
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 多入口配置

module.exports = {
  entry: {
    main: './src/index.js',
    vendor: './src/vendor.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js' // 使用内容哈希,便于缓存
  }
};
1
2
3
4
5
6
7
8
9
10

# 资源解析配置

module.exports = {
  // ...
  resolve: {
    // 自动解析这些扩展名
    extensions: ['.js', '.jsx', '.json'],
    
    // 路径别名
    alias: {
      '@': path.resolve(__dirname, 'src'),
      'components': path.resolve(__dirname, 'src/components')
    }
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13

# 加载器(Loader)配置

# JavaScript和TypeScript处理

module.exports = {
  // ...
  module: {
    rules: [
      // JavaScript处理
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      
      // TypeScript处理
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# CSS和预处理器

module.exports = {
  // ...
  module: {
    rules: [
      // CSS处理
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      
      // SCSS处理
      {
        test: /\.scss$/,
        use: [
          'style-loader', // 将CSS插入到DOM
          'css-loader',   // 解析CSS导入
          'postcss-loader', // 处理CSS兼容性
          'sass-loader'   // 将SCSS编译为CSS
        ]
      },
      
      // Less处理
      {
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      }
    ]
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 文件和资源加载

module.exports = {
  // ...
  module: {
    rules: [
      // 图片处理
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'images/[name].[hash][ext]'
        }
      },
      
      // 字体处理
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'fonts/[name].[hash][ext]'
        }
      },
      
      // XML加载
      {
        test: /\.xml$/,
        use: ['xml-loader']
      }
    ]
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 高级加载器配置

module.exports = {
  // ...
  module: {
    rules: [
      // 链式加载器
      {
        test: /\.scss$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader, // 从JS中提取CSS
            options: {
              publicPath: '../' // 修正路径问题
            }
          },
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2, // 确保在css-loader前应用其它加载器
              modules: {
                localIdentName: '[name]__[local]___[hash:base64:5]' // CSS模块化命名
              }
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  ['autoprefixer', {}]
                ]
              }
            }
          },
          'sass-loader'
        ]
      },
      
      // 内联器 - 小文件转换为数据URL
      {
        test: /\.(png|jpg|gif)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024 // 8KB以下转换为Base64
          }
        }
      }
    ]
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# 插件(Plugins)使用

# 常用插件配置

const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    // 清理输出目录
    new CleanWebpackPlugin(),
    
    // 生成HTML文件并自动引入资源
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    }),
    
    // 提取CSS到单独文件
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash].css'
    }),
    
    // 复制静态资源
    new CopyWebpackPlugin({
      patterns: [
        { from: 'src/assets', to: 'assets' }
      ]
    })
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 环境变量与定义常量

const { DefinePlugin } = require('webpack');
const dotenv = require('dotenv');

// 加载环境变量
const env = dotenv.config().parsed || {};
const envKeys = Object.keys(env).reduce((prev, next) => {
  prev[`process.env.${next}`] = JSON.stringify(env[next]);
  return prev;
}, {});

module.exports = {
  // ...
  plugins: [
    // 定义全局常量
    new DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      'API_URL': JSON.stringify('https://api.example.com'),
      ...envKeys
    })
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 高级插件配置

const WorkboxPlugin = require('workbox-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    // ServiceWorker生成
    new WorkboxPlugin.GenerateSW({
      clientsClaim: true,
      skipWaiting: true
    }),
    
    // 打包分析
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      openAnalyzer: false,
      reportFilename: 'bundle-report.html'
    }),
    
    // Gzip压缩
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css|html|svg)$/,
      threshold: 10240, // 只有大于10KB的资源才会被处理
      minRatio: 0.8 // 只有压缩率小于这个值的资源才会被处理
    })
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 优化构建性能

# 减小打包体积

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

module.exports = {
  // ...
  optimization: {
    minimize: true,
    minimizer: [
      // 压缩JavaScript
      new TerserPlugin({
        terserOptions: {
          parse: {
            ecma: 8,
          },
          compress: {
            ecma: 5,
            warnings: false,
            comparisons: false,
            inline: 2,
          },
          mangle: {
            safari10: true,
          },
          output: {
            ecma: 5,
            comments: false,
            ascii_only: true,
          },
        },
        parallel: true,
      }),
      
      // 压缩CSS
      new CssMinimizerPlugin({
        minimizerOptions: {
          preset: [
            'default',
            {
              discardComments: { removeAll: true },
            },
          ],
        },
      }),
    ],
    
    // 抽离公共模块
    splitChunks: {
      chunks: 'all',
      name: false,
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        },
        common: {
          name: 'common',
          minChunks: 2,
          chunks: 'all',
          priority: -20,
          reuseExistingChunk: true
        }
      }
    },
    
    // 将runtime代码提取到单独的文件
    runtimeChunk: 'single'
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

# 提升构建速度

const path = require('path');
const webpack = require('webpack');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');

module.exports = {
  // ...
  // 缓存
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename] // 当构建依赖的config文件(通过 require 依赖)内容发生变化时,缓存失效
    }
  },
  
  // 优化解析
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
    modules: [path.resolve(__dirname, 'src'), 'node_modules'],
    // 告诉webpack哪些模块不做解析处理
    noParse: /jquery|lodash/
  },
  
  // 设置loader的包含和排除路径,减小解析范围
  module: {
    rules: [
      {
        test: /\.js$/,
        include: path.resolve(__dirname, 'src'),
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  },
  
  plugins: [
    // 使用缓存提高二次构建速度
    new HardSourceWebpackPlugin(),
    
    // 只编译需要的国际化文件
    new webpack.ContextReplacementPlugin(
      /moment[/\\]locale$/,
      /zh-cn|en-us/
    )
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 代码分割与懒加载

# 动态导入和代码分割

// webpack.config.js
module.exports = {
  // ...
  optimization: {
    splitChunks: {
      chunks: 'all',
      maxInitialRequests: Infinity,
      minSize: 0,
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name(module) {
            // 获取包名
            const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
            // npm包名称可能包含@,进行替换
            return `npm.${packageName.replace('@', '')}`;
          },
        },
      },
    },
  }
};

// 在代码中使用动态导入
// App.js
const loadAdminModule = () => import(/* webpackChunkName: "admin" */ './admin');

function App() {
  const [AdminModule, setAdminModule] = useState(null);

  const loadAdmin = async () => {
    const module = await loadAdminModule();
    setAdminModule(module.default);
  };

  return (
    <div>
      {!AdminModule ? (
        <button onClick={loadAdmin}>加载管理模块</button>
      ) : (
        <AdminModule />
      )}
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 预加载与预获取

// 预获取:浏览器空闲时获取
import(/* webpackPrefetch: true */ './path/to/LoginModal.js');

// 预加载:与父chunk并行加载
import(/* webpackPreload: true */ './path/to/ChartingLibrary.js');
1
2
3
4
5

# 环境配置管理

# 多环境配置文件

// webpack.common.js - 公共配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  }
};

// webpack.dev.js - 开发环境
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
  devtool: 'eval-source-map',
  devServer: {
    static: './dist',
    hot: true,
    port: 3000,
    open: true
  }
});

// webpack.prod.js - 生产环境
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = merge(common, {
  mode: 'production',
  devtool: 'source-map',
  output: {
    publicPath: '/' // CDN前缀可以在这里添加
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash].css'
    })
  ],
  optimization: {
    minimizer: [
      new CssMinimizerPlugin(),
      new TerserPlugin()
    ]
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# 环境变量使用

// package.json
{
  "scripts": {
    "dev": "webpack serve --config webpack.dev.js --env development",
    "build:dev": "webpack --config webpack.prod.js --env development",
    "build:stage": "webpack --config webpack.prod.js --env staging",
    "build:prod": "webpack --config webpack.prod.js --env production"
  }
}

// webpack.config.js
module.exports = (env) => {
  console.log('环境变量:', env);
  
  return {
    // 基于环境变量的动态配置
    mode: env.production ? 'production' : 'development',
    plugins: [
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify(env.production ? 'production' : 'development'),
        'process.env.API_URL': JSON.stringify(
          env.production 
            ? 'https://api.example.com/v1' 
            : env.staging 
              ? 'https://staging-api.example.com/v1'
              : 'http://localhost:3001/v1'
        )
      })
    ]
  };
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 模块热替换(HMR)

# 基础HMR配置

// webpack.config.js
const webpack = require('webpack');

module.exports = {
  // ...
  devServer: {
    static: './dist',
    hot: true, // 启用HMR
    port: 3000,
    open: true
  },
  plugins: [
    // 添加HMR插件
    new webpack.HotModuleReplacementPlugin()
  ]
};

// index.js中启用HMR
if (module.hot) {
  module.hot.accept();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# React热更新配置

// webpack.config.js
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
            plugins: ['react-hot-loader/babel']
          }
        }
      }
    ]
  }
};

// App.js
import { hot } from 'react-hot-loader/root';

const App = () => (
  <div>
    <h1>Hello, World!</h1>
  </div>
);

export default hot(App);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# Vue热更新配置

// webpack.config.js
const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
};

// main.js
import Vue from 'vue';
import App from './App.vue';

new Vue({
  render: h => h(App)
}).$mount('#app');

// Vue组件中无需特殊配置,vue-loader已支持HMR
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 高级配置案例

# 服务端渲染(SSR)配置

// webpack.client.js
const path = require('path');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');

module.exports = merge(baseConfig, {
  entry: './src/client/index.js',
  output: {
    path: path.resolve(__dirname, 'dist/public'),
    filename: '[name].[contenthash].js',
    publicPath: '/'
  },
  plugins: [
    new WebpackManifestPlugin({
      fileName: 'asset-manifest.json'
    })
  ]
});

// webpack.server.js
const path = require('path');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.js');
const nodeExternals = require('webpack-node-externals');

module.exports = merge(baseConfig, {
  target: 'node',
  entry: './src/server/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'server.js',
    publicPath: '/'
  },
  externals: [nodeExternals()], // 排除node_modules
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'isomorphic-style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true,
              esModule: false,
              exportOnlyLocals: true
            }
          }
        ]
      }
    ]
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# 微前端配置

// webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/components/Button',
        './Header': './src/components/Header'
      },
      shared: {
        react: {
          singleton: true,
          requiredVersion: '^17.0.0'
        },
        'react-dom': {
          singleton: true,
          requiredVersion: '^17.0.0'
        }
      }
    })
  ]
};

// 主应用配置
module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        app1: 'app1@http://localhost:3001/remoteEntry.js'
      },
      shared: {
        react: {
          singleton: true,
          requiredVersion: '^17.0.0'
        },
        'react-dom': {
          singleton: true,
          requiredVersion: '^17.0.0'
        }
      }
    })
  ]
};

// 在宿主应用中使用远程组件
import React, { lazy, Suspense } from 'react';

// 动态导入远程组件
const RemoteButton = lazy(() => import('app1/Button'));

function App() {
  return (
    <div>
      <h1>主应用</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <RemoteButton />
      </Suspense>
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

# PWA配置

const WorkboxPlugin = require('workbox-webpack-plugin');
const WebpackPwaManifest = require('webpack-pwa-manifest');
const path = require('path');

module.exports = {
  // ...
  plugins: [
    // 生成Web应用清单
    new WebpackPwaManifest({
      name: '我的PWA应用',
      short_name: 'PWA App',
      description: 'PWA示例应用',
      background_color: '#ffffff',
      theme_color: '#31a8f8',
      icons: [
        {
          src: path.resolve('src/assets/icon.png'),
          sizes: [96, 128, 192, 256, 384, 512],
          destination: path.join('icons')
        }
      ]
    }),
    
    // 生成Service Worker
    new WorkboxPlugin.GenerateSW({
      clientsClaim: true,
      skipWaiting: true,
      runtimeCaching: [{
        urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
        handler: 'CacheFirst',
        options: {
          cacheName: 'images',
          expiration: {
            maxEntries: 60,
            maxAgeSeconds: 30 * 24 * 60 * 60 // 30天
          }
        }
      }, {
        urlPattern: /^https:\/\/api\.example\.com/,
        handler: 'NetworkFirst',
        options: {
          cacheName: 'api-cache',
          networkTimeoutSeconds: 10,
          expiration: {
            maxEntries: 50,
            maxAgeSeconds: 5 * 60 // 5分钟
          },
          cacheableResponse: {
            statuses: [0, 200]
          }
        }
      }]
    })
  ]
};

// index.js中注册Service Worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
      .then(registration => {
        console.log('SW registered: ', registration);
      })
      .catch(error => {
        console.log('SW registration failed: ', error);
      });
  });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

# Webpack 5新特性

# 资源模块配置

module.exports = {
  // ...
  module: {
    rules: [
      // 资源模块-替代file-loader
      {
        test: /\.(png|jpg|gif)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'images/[hash][ext][query]'
        }
      },
      
      // 内联资源-替代url-loader
      {
        test: /\.svg$/i,
        type: 'asset/inline'
      },
      
      // 文本资源-替代raw-loader
      {
        test: /\.txt$/i,
        type: 'asset/source'
      },
      
      // 自动选择-小于8kb时内联,否则作为独立资源
      {
        test: /\.(woff|woff2)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024
          }
        }
      }
    ]
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 持久化缓存

module.exports = {
  // ...
  cache: {
    type: 'filesystem', // 使用文件系统缓存
    buildDependencies: {
      config: [__filename], // 将配置文件添加为构建依赖,当配置更改时缓存失效
    },
    cacheDirectory: path.resolve(__dirname, '.webpack_cache'), // 自定义缓存目录
    name: 'production-cache', // 缓存名称,用于多环境下的缓存区分
    version: '1.0' // 自定义版本,手动控制缓存失效
  }
};
1
2
3
4
5
6
7
8
9
10
11
12

# 模块联邦 (Module Federation)

// host应用配置
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        remote1: 'remote1@http://localhost:3001/remoteEntry.js',
        remote2: 'remote2@http://localhost:3002/remoteEntry.js',
      },
      shared: {
        react: { 
          singleton: true,
          requiredVersion: '^18.0.0'
        },
        'react-dom': { 
          singleton: true,
          requiredVersion: '^18.0.0'
        }
      }
    })
  ]
};

// 远程应用1配置
module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: 'remote1',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/components/Button.js',
        './Card': './src/components/Card.js'
      },
      shared: {
        react: { 
          singleton: true,
          requiredVersion: '^18.0.0'
        },
        'react-dom': { 
          singleton: true,
          requiredVersion: '^18.0.0'
        }
      }
    })
  ]
};

// 远程应用2配置
module.exports = {
  // ...
  plugins: [
    new ModuleFederationPlugin({
      name: 'remote2',
      filename: 'remoteEntry.js',
      exposes: {
        './Header': './src/components/Header.js',
        './Footer': './src/components/Footer.js'
      },
      remotes: {
        remote1: 'remote1@http://localhost:3001/remoteEntry.js',
      },
      shared: {
        react: { 
          singleton: true,
          requiredVersion: '^18.0.0'
        },
        'react-dom': { 
          singleton: true,
          requiredVersion: '^18.0.0'
        }
      }
    })
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

# Tree Shaking增强

// webpack.config.js
module.exports = {
  // ...
  optimization: {
    usedExports: true, // 启用树摇,标记未使用的导出
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            pure_funcs: ['console.log'] // 删除特定函数调用
          }
        }
      })
    ]
  }
};

// package.json
{
  "name": "my-project",
  "sideEffects": [
    "*.css",
    "*.scss",
    "src/polyfills.js"
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 常见问题与解决方案

# 解决路径别名问题

// webpack.config.js
module.exports = {
  // ...
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
      'components': path.resolve(__dirname, 'src/components')
    }
  }
};

// 配置VSCode或其他编辑器,添加jsconfig.json或tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "components/*": ["src/components/*"]
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 解决大型依赖包问题

module.exports = {
  // ...
  resolve: {
    alias: {
      'lodash-es': 'lodash' // 使用较小的替代库
    }
  },
  plugins: [
    // 使用IgnorePlugin忽略不需要的语言包
    new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/
    }),
    
    // 或使用ContextReplacementPlugin只加载特定语言
    new webpack.ContextReplacementPlugin(
      /moment[/\\]locale$/,
      /zh-cn|en-us/
    )
  ]
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 解决构建慢的问题

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();

// 用SpeedMeasurePlugin包装配置以测量构建速度
module.exports = smp.wrap({
  // ...原始配置
});

// 或者使用多线程加速构建
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'thread-loader',
            options: {
              workers: require('os').cpus().length - 1,
            }
          },
          'babel-loader'
        ]
      }
    ]
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 处理内存溢出问题

// package.json脚本
{
  "scripts": {
    "build": "node --max-old-space-size=8192 node_modules/.bin/webpack --config webpack.prod.js"
  }
}

// 或使用多进程构建大型项目
const parallelUglifyPlugin = require('webpack-parallel-uglify-plugin');

module.exports = {
  // ...
  optimization: {
    minimizer: [
      new parallelUglifyPlugin({
        cacheDir: '.cache/',
        uglifyJS: {
          output: {
            comments: false
          },
          compress: {
            drop_console: true
          }
        }
      })
    ]
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

通过本文的详解,你应该对Webpack的配置有了全面的了解,从基础概念到高级用法,再到性能优化和最佳实践。随着项目的不断发展,可以根据需要逐步应用这些配置,打造出优质的前端构建流程。

Last Updated: 2025-03-11T08:38:33.000Z