- 发布于
Webpack 5 完全配置指南:现代前端构建工具详解
- 作者

- 姓名
- 全能波
- GitHub
- @weicracker
Webpack 5 完全配置指南:现代前端构建工具详解
Webpack 5 带来了许多激动人心的新特性,包括模块联邦、更好的缓存策略、Tree Shaking 优化等。本文将深入探讨 Webpack 5 的配置和最佳实践。
Webpack 5 新特性概览
1. 模块联邦(Module Federation)
// webpack.config.js - 主应用
const ModuleFederationPlugin = require('@module-federation/webpack')
module.exports = {
mode: 'development',
devServer: {
port: 3000,
},
plugins: [
new ModuleFederationPlugin({
name: 'shell',
remotes: {
mfApp: 'mfApp@http://localhost:3001/remoteEntry.js',
userApp: 'userApp@http://localhost:3002/remoteEntry.js',
},
}),
],
}
// webpack.config.js - 微前端应用
module.exports = {
mode: 'development',
devServer: {
port: 3001,
},
plugins: [
new ModuleFederationPlugin({
name: 'mfApp',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button',
'./Header': './src/components/Header',
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true },
},
}),
],
}
2. 持久化缓存
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename],
},
cacheDirectory: path.resolve(__dirname, '.webpack-cache'),
compression: 'gzip',
},
// 优化缓存策略
optimization: {
moduleIds: 'deterministic',
chunkIds: 'deterministic',
},
}
基础配置结构
1. 完整的配置文件
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
module.exports = (env, argv) => {
const isProduction = argv.mode === 'production'
return {
entry: {
main: './src/index.js',
vendor: ['react', 'react-dom'],
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: isProduction ? '[name].[contenthash:8].js' : '[name].js',
chunkFilename: isProduction ? '[name].[contenthash:8].chunk.js' : '[name].chunk.js',
clean: true,
publicPath: '/',
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
},
},
module: {
rules: [
// JavaScript/TypeScript
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }],
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-syntax-dynamic-import',
],
},
},
},
// CSS/SCSS
{
test: /\.(css|scss|sass)$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
options: {
modules: {
auto: true,
localIdentName: isProduction
? '[hash:base64:8]'
: '[name]__[local]--[hash:base64:5]',
},
importLoaders: 2,
},
},
'postcss-loader',
'sass-loader',
],
},
// 图片和字体
{
test: /\.(png|jpe?g|gif|svg|webp)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024, // 8KB
},
},
generator: {
filename: 'images/[name].[hash:8][ext]',
},
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: 'fonts/[name].[hash:8][ext]',
},
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
minify: isProduction
? {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
}
: false,
}),
...(isProduction
? [
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
chunkFilename: '[name].[contenthash:8].chunk.css',
}),
]
: []),
],
optimization: {
minimize: isProduction,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: isProduction,
},
},
}),
new CssMinimizerPlugin(),
],
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
priority: 10,
},
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
priority: 5,
reuseExistingChunk: true,
},
},
},
runtimeChunk: {
name: 'runtime',
},
},
devServer: {
port: 3000,
hot: true,
open: true,
historyApiFallback: true,
compress: true,
static: {
directory: path.join(__dirname, 'public'),
},
},
devtool: isProduction ? 'source-map' : 'eval-cheap-module-source-map',
}
}
2. PostCSS 配置
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer'),
require('postcss-preset-env')({
stage: 3,
features: {
'nesting-rules': true,
'custom-properties': true,
},
}),
...(process.env.NODE_ENV === 'production'
? [
require('cssnano')({
preset: [
'default',
{
discardComments: { removeAll: true },
},
],
}),
]
: []),
],
}
高级配置技巧
1. 环境变量配置
const webpack = 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 webpack.DefinePlugin({
...envKeys,
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
],
}
2. 多页面应用配置
const glob = require('glob')
function getEntries() {
const entries = {}
const pages = glob.sync('./src/pages/*/index.js')
pages.forEach((page) => {
const pageName = page.match(/\/pages\/(.+)\/index\.js$/)[1]
entries[pageName] = page
})
return entries
}
function getHtmlPlugins() {
const entries = getEntries()
return Object.keys(entries).map(
(name) =>
new HtmlWebpackPlugin({
template: `./src/pages/${name}/index.html`,
filename: `${name}.html`,
chunks: [name, 'vendor', 'common'],
minify: process.env.NODE_ENV === 'production',
})
)
}
module.exports = {
entry: getEntries(),
plugins: [...getHtmlPlugins()],
}
3. 代码分割策略
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
minSize: 20000,
maxSize: 244000,
cacheGroups: {
// 第三方库
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
reuseExistingChunk: true,
},
// React 相关
react: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'react',
priority: 20,
reuseExistingChunk: true,
},
// UI 库
antd: {
test: /[\\/]node_modules[\\/]antd[\\/]/,
name: 'antd',
priority: 15,
reuseExistingChunk: true,
},
// 工具库
utils: {
test: /[\\/]node_modules[\\/](lodash|moment|dayjs)[\\/]/,
name: 'utils',
priority: 15,
reuseExistingChunk: true,
},
// 公共代码
common: {
name: 'common',
minChunks: 2,
priority: 5,
reuseExistingChunk: true,
},
},
},
},
}
性能优化配置
1. Tree Shaking 优化
module.exports = {
mode: 'production',
optimization: {
usedExports: true,
sideEffects: false, // 或者指定具体文件 ['*.css', '*.scss']
},
// 确保 babel 不转换 ES6 模块
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { modules: false }], // 关键配置
],
},
},
},
],
},
}
2. 构建分析和优化
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
module.exports = smp.wrap({
plugins: [
// 只在分析时启用
...(process.env.ANALYZE
? [
new BundleAnalyzerPlugin({
analyzerMode: 'server',
openAnalyzer: true,
}),
]
: []),
],
// 优化构建性能
resolve: {
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
symlinks: false,
},
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
},
],
},
})
3. 预加载和预获取
// 在代码中使用魔法注释
import(
/* webpackChunkName: "lodash" */
/* webpackPreload: true */
'lodash'
).then(({ default: _ }) => {
// 使用 lodash
})
// 预获取
import(
/* webpackChunkName: "chart" */
/* webpackPrefetch: true */
'./Chart'
).then((Chart) => {
// 使用图表组件
})
开发体验优化
1. 热更新配置
module.exports = {
devServer: {
hot: true,
liveReload: false, // 禁用 live reload,只使用 HMR
},
plugins: [new webpack.HotModuleReplacementPlugin()],
}
// 在应用代码中
if (module.hot) {
module.hot.accept('./App', () => {
// 重新渲染应用
render()
})
}
2. 错误处理和调试
module.exports = {
stats: {
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false,
},
plugins: [
new webpack.ProgressPlugin((percentage, message, ...args) => {
console.log(`${Math.round(percentage * 100)}%`, message, ...args)
}),
],
// 开发环境的 source map
devtool: 'eval-cheap-module-source-map',
}
部署配置
1. 生产环境优化
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
mode: 'production',
plugins: [
// Gzip 压缩
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 8192,
minRatio: 0.8,
}),
// 清理输出目录
new CleanWebpackPlugin(),
],
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
},
},
}),
],
},
}
2. CDN 配置
module.exports = {
output: {
publicPath: process.env.NODE_ENV === 'production' ? 'https://cdn.example.com/assets/' : '/',
},
externals: {
react: 'React',
'react-dom': 'ReactDOM',
lodash: '_',
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
templateParameters: {
cdnLinks:
process.env.NODE_ENV === 'production'
? [
'https://unpkg.com/react@17/umd/react.production.min.js',
'https://unpkg.com/react-dom@17/umd/react-dom.production.min.js',
'https://unpkg.com/lodash@4/lodash.min.js',
]
: [],
},
}),
],
}
最佳实践总结
1. 配置文件组织
// webpack/webpack.common.js
// webpack/webpack.dev.js
// webpack/webpack.prod.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
module.exports = merge(common, {
mode: 'development',
// 开发环境特定配置
})
2. 性能监控
// 添加构建时间监控
const start = Date.now()
module.exports = {
plugins: [
{
apply: (compiler) => {
compiler.hooks.done.tap('BuildTimePlugin', () => {
console.log(`构建完成,耗时: ${Date.now() - start}ms`)
})
},
},
],
}
总结
Webpack 5 为现代前端开发提供了强大的构建能力:
- 模块联邦:微前端架构的完美解决方案
- 持久化缓存:显著提升构建性能
- 更好的 Tree Shaking:减少包体积
- 资源模块:内置的资源处理能力
- 改进的代码分割:更智能的分包策略
掌握这些配置技巧,你就能构建出高性能、可维护的现代前端应用!
Webpack 5 是现代前端工程化的核心工具,值得深入学习和实践。