Webpack 5 高性能配置方案
下面是一个针对现代Web开发的通用Webpack 5配置方案,经过优化,支持多种框架和技术栈,包含最佳实践和性能优化策略。
// webpack.config.js - 通用高性能配置
const path = require('path');
const { DefinePlugin, ProgressPlugin } = require('webpack');
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');
const CopyPlugin = require('copy-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');module.exports = (env, argv) => {const isProduction = argv.mode === 'production';const isDev = !isProduction;const isAnalyze = env && env.analyze;return {// 入口配置(支持多入口和动态导入)entry: {main: './src/index.js',vendor: ['react', 'react-dom'] // 常用库分离},// 输出配置(缓存策略优化)output: {filename: isProduction ? 'js/[name].[contenthash:8].bundle.js' : 'js/[name].bundle.js',chunkFilename: isProduction ? 'js/[name].[contenthash:8].chunk.js' : 'js/[name].chunk.js',path: path.resolve(__dirname, 'dist'),publicPath: '/',assetModuleFilename: 'assets/[hash][ext][query]',},// 开发环境配置devtool: isProduction ? 'source-map' : 'cheap-module-source-map',mode: isProduction ? 'production' : 'development',// 模块规则配置module: {rules: [// JS/JSX处理(包含缓存和多线程支持){test: /\.(js|jsx)$/,exclude: /node_modules/,use: [{loader: 'babel-loader',options: {cacheDirectory: true,presets: [['@babel/preset-env', { modules: false,useBuiltIns: 'usage',corejs: 3 }],'@babel/preset-react'],plugins: ['@babel/plugin-transform-runtime','@babel/plugin-proposal-class-properties',['babel-plugin-import', { "libraryName": "antd","libraryDirectory": "es","style": true }]]}},{loader: 'thread-loader',options: {workers: require('os').cpus().length - 1,poolTimeout: isDev ? Infinity : 2000}}]},// TypeScript支持(可选){test: /\.tsx?$/,use: 'ts-loader',exclude: /node_modules/},// CSS模块(本地作用域){test: /\.module\.(sa|sc|c)ss$/,use: [isProduction ? MiniCssExtractPlugin.loader : 'style-loader',{loader: 'css-loader',options: {modules: {localIdentName: isProduction ? '[hash:base64:8]' : '[path][name]__[local]',exportLocalsConvention: 'camelCaseOnly'},sourceMap: isDev}},'postcss-loader',{loader: 'sass-loader',options: {sourceMap: isDev}}]},// 全局CSS(无模块){test: /\.(sa|sc|c)ss$/,exclude: /\.module\.css$/,use: [isProduction ? MiniCssExtractPlugin.loader : 'style-loader',{loader: 'css-loader',options: {sourceMap: isDev}},'postcss-loader',{loader: 'sass-loader',options: {sourceMap: isDev}}]},// 图片资源(自动优化){test: /\.(png|jpe?g|gif|webp|avif)(\?.*)?$/i,type: 'asset',parser: {dataUrlCondition: {maxSize: 8 * 1024 // 小于8KB转为Base64}},generator: {filename: 'images/[contenthash][ext][query]'}},// SVG处理(可选择转为组件){test: /\.svg(\?v=\w+)?$/,oneOf: [{resourceQuery: /component/,use: ['@svgr/webpack','url-loader']},{type: 'asset/resource',generator: {filename: 'svg/[contenthash][ext][query]'}}]},// 字体资源{test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,type: 'asset/resource',generator: {filename: 'fonts/[contenthash][ext][query]'}},// 媒体资源(视频/音频){test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,type: 'asset/resource',generator: {filename: 'media/[contenthash][ext][query]'}},// WebAssembly支持{test: /\.wasm$/,type: 'webassembly/async'},// MDX支持(文档驱动){test: /\.mdx?$/,use: [{loader: 'babel-loader',options: {presets: ['@babel/preset-react']}},'@mdx-js/loader']}]},// 插件系统plugins: [// 构建进度显示new ProgressPlugin({entries: true,modules: true,modulesCount: 100,profile: true}),// 清理构建目录new CleanWebpackPlugin({cleanOnceBeforeBuildPatterns: ['**/*','!manifest.json','!robots.txt','!favicon.ico'],protectWebpackAssets: false}),// HTML模板new HtmlWebpackPlugin({title: '现代Web应用',template: './public/index.html',filename: 'index.html',favicon: './public/favicon.ico',inject: 'body',minify: isProduction ? {collapseWhitespace: true,removeComments: true,removeRedundantAttributes: true} : false,scriptLoading: 'module',meta: {viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no','theme-color': '#4285f4'},chunks: ['main', 'vendor']}),// CSS提取new MiniCssExtractPlugin({filename: 'css/[name].[contenthash:8].css',chunkFilename: 'css/[name].[contenthash:8].chunk.css',ignoreOrder: true}),// 全局常量new DefinePlugin({'process.env.NODE_ENV': JSON.stringify(isProduction ? 'production' : 'development'),'process.env.APP_VERSION': JSON.stringify(process.env.npm_package_version || '1.0.0'),'__IS_DEVELOPMENT__': isDev}),// 复制静态资源new CopyPlugin({patterns: [{ from: 'public',to: '.',globOptions: {ignore: ['**/index.html']}}]}),// Gzip压缩isProduction && new CompressionPlugin({test: /\.(js|css|html|svg|json)$/,filename: '[path][base].gz',algorithm: 'gzip',threshold: 1024 * 10, // 10KB以上压缩minRatio: 0.8,exclude: /\.(br|gz)$/}),// Brotli压缩(更优算法)isProduction && new CompressionPlugin({test: /\.(js|css|html|svg|json)$/,filename: '[path][base].br',algorithm: 'brotliCompress',compressionOptions: { level: 11 },threshold: 1024 * 10,minRatio: 0.8,exclude: /\.(br|gz)$/}),// 包分析工具isAnalyze && new BundleAnalyzerPlugin({analyzerMode: 'static',openAnalyzer: false,reportFilename: '../report/bundle-report.html'})].filter(Boolean),// 优化配置optimization: {minimize: isProduction,minimizer: [// JS压缩new TerserPlugin({parallel: true,extractComments: false,terserOptions: {compress: {drop_console: isProduction,drop_debugger: isProduction,comparisons: false},mangle: true,output: {ecma: 5,comments: false,ascii_only: true}}}),// CSS压缩new CssMinimizerPlugin({minimizerOptions: {preset: ["default", {discardComments: { removeAll: true },cssDeclarationSorter: true}]},parallel: 4})],// 代码分割splitChunks: {chunks: 'all',minSize: 30000,minChunks: 1,maxAsyncRequests: 20,maxInitialRequests: 20,automaticNameDelimiter: '~',cacheGroups: {// 分离第三方库vendors: {test: /[\\/]node_modules[\\/]/,name(module) {const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];return `npm.${packageName.replace('@', '')}`;},priority: -10},// 分离公共模块common: {name: 'common',minChunks: 2,priority: -20,reuseExistingChunk: true},// CSS文件优化styles: {name: "styles",type: "css/mini-extract",chunks: "all",enforce: true}}},// 运行时单独分离runtimeChunk: {name: entrypoint => `runtime-${entrypoint.name}`},// 模块ID算法优化moduleIds: isProduction ? 'deterministic' : 'named',chunkIds: isProduction ? 'deterministic' : 'named'},// 开发服务器devServer: {static: {directory: path.join(__dirname, 'public'),},compress: true,port: 9000,hot: true,open: '/',historyApiFallback: true,client: {overlay: {errors: true,warnings: false},progress: true,reconnect: 5},proxy: {'/api': {target: 'http://localhost:3000',changeOrigin: true,pathRewrite: { '^/api': '' }}},devMiddleware: {writeToDisk: true // 用于调试生成的文件}},// 解析配置(重要!)resolve: {extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.scss', '.css'],alias: {'@': path.resolve('src'),'@assets': path.resolve('src/assets'),'@components': path.resolve('src/components'),'@pages': path.resolve('src/pages'),'@utils': path.resolve('src/utils'),'@store': path.resolve('src/store'),'@styles': path.resolve('src/styles'),// 添加框架别名支持'react-native$': 'react-native-web'},fallback: {"crypto": require.resolve("crypto-browserify"),"stream": require.resolve("stream-browserify"),"buffer": require.resolve("buffer/"),"assert": require.resolve("assert/"),"https": require.resolve("https-browserify"),"http": require.resolve("stream-http"),"url": require.resolve("url/"),"zlib": require.resolve("browserify-zlib"),"path": require.resolve("path-browserify"),"fs": false}},// 外部依赖(优化构建大小)externals: isProduction ? {'react': 'React','react-dom': 'ReactDOM','lodash': '_','moment': 'moment'} : {},// 性能设置performance: {maxAssetSize: process.env.NODE_ENV === 'production' ? 512 * 1024 : Infinity,maxEntrypointSize: process.env.NODE_ENV === 'production' ? 512 * 1024 : Infinity,hints: process.env.NODE_ENV === 'production' ? 'warning' : false},// 实验性特性experiments: {syncWebAssembly: true,asyncWebAssembly: true,topLevelAwait: true}};
};
关键优化策略说明
🚀 性能优化
-
代码分割优化
splitChunks: {cacheGroups: {vendors: {test: /[\\/]node_modules[\\/]/,name: module => `npm.${module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1].replace('@', '')}`}} }
-
缓存策略
output: {filename: 'js/[name].[contenthash:8].bundle.js',chunkFilename: 'js/[name].[contenthash:8].chunk.js' }
-
多线程处理
use: [{ loader: 'thread-loader', options: { workers: require('os').cpus().length - 1 } },'babel-loader' ]
📦 模块处理优化
-
智能资源处理
- 图片:<8KB自动转为Base64
- SVG:可选择作为组件导入或作为资源
{test: /\.svg(\?v=\w+)?$/,oneOf: [{ resourceQuery: /component/, use: ['@svgr/webpack'] },{ type: 'asset/resource' }] }
-
多格式支持
- WebAssembly (WASM)
- MDX (Markdown + JSX)
- TypeScript
🌈 开发体验优化
-
渐进式加载提示
new HtmlWebpackPlugin({scriptLoading: 'module',preload: '**/*.css',prefetch: ['app.chunk.js'] })
-
开发服务器优化
devServer: {client: {overlay: { errors: true, warnings: false },progress: true,reconnect: 5} }
-
HOT热更新与模块联邦支持
💡 高级特性支持
-
WebAssembly集成
experiments: {syncWebAssembly: true,asyncWebAssembly: true,topLevelAwait: true }
-
模块联邦支持
// 可添加ModuleFederationPlugin new ModuleFederationPlugin({name: 'myApp',filename: 'remoteEntry.js',exposes: {'./Button': './src/components/Button.js'},remotes: {otherApp: 'otherApp@https://other-domain.com/remoteEntry.js'} })
执行脚本 (package.json)
{"scripts": {"dev": "webpack serve --mode development","build": "webpack --mode production","build:analyze": "webpack --mode production --env analyze","start": "webpack serve --open","lint": "eslint . --ext .js,.jsx"},"devDependencies": {"webpack": "^5.89.0","webpack-cli": "^5.1.4","webpack-dev-server": "^4.15.1","html-webpack-plugin": "^5.5.3","clean-webpack-plugin": "^4.0.0","thread-loader": "^4.0.2","babel-loader": "^9.1.3","@babel/core": "^7.22.11","@babel/preset-env": "^7.22.10","@babel/preset-react": "^7.22.5","css-loader": "^6.8.1","sass-loader": "^13.3.2","postcss-loader": "^8.0.0","mini-css-extract-plugin": "^2.7.6","terser-webpack-plugin": "^5.3.9","css-minimizer-webpack-plugin": "^6.2.0","copy-webpack-plugin": "^11.0.0","compression-webpack-plugin": "^10.0.0","webpack-bundle-analyzer": "^4.9.1","sass": "^1.67.0"},"dependencies": {"antd": "^5.5.0","react": "^18.2.0","react-dom": "^18.2.0","@babel/runtime": "^7.22.11"}
}
最佳实践指南
1. 项目结构推荐
├── public # 静态资源
├── src # 源代码
│ ├── assets # 静态资源
│ ├── components # 通用组件
│ ├── pages # 页面组件
│ ├── layouts # 布局组件
│ ├── store # 状态管理
│ ├── services # API服务
│ ├── utils # 工具函数
│ ├── styles # 全局样式
│ ├── hooks # 自定义Hooks
│ ├── config # 配置文件
│ ├── types # TS类型定义
│ ├── app.js # 主应用组件
│ └── index.js # 入口文件
2. 多环境配置
创建webpack.config.js
和webpack.prod.config.js
文件:
// webpack.prod.config.js
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.config.js');module.exports = (env) => merge(commonConfig(env), {mode: 'production',optimization: {minimize: true,// 生产环境特有优化},plugins: [// 生产环境特有插件]
});
3. 路由懒加载(React示例)
import React, { lazy, Suspense } from 'react';const Dashboard = lazy(() => import(/* webpackChunkName: "dashboard" */ './pages/Dashboard'));
const Settings = lazy(() => import(/* webpackChunkName: "settings" */ './pages/Settings'));function App() {return (<Suspense fallback={<div>Loading...</div>}><Routes><Route path="/" element={<Dashboard />} /><Route path="/settings" element={<Settings />} /></Routes></Suspense>);
}
4. 高级缓存控制
通过服务端实现长缓存策略:
# Nginx配置
location ~* \.(js|css)$ {expires 365d;add_header Cache-Control "public, immutable";
}location ~* \.(woff|woff2|jpg|png|svg)$ {expires 30d;add_header Cache-Control "public";
}
注意事项
- Tree Shaking确保在package.json中添加
"sideEffects": false
属性 - 使用ES6模块语法让Webpack更好的优化
- 避免在库中使用
module.exports = ...
导出 - 在大型项目中使用TS和类型检查
- 定期运行
npm run build:analyze
检查包大小
本配置适用于Web应用开发,支持Vue等主流框架(适当调整loader即可),经过全面优化,兼顾开发体验和生产性能。