18.1 基于 Webpack 构建
18.1 基于 Webpack 构建
现代单页应用(SPA)开发离不开强大的构建工具,Webpack 作为最主流的模块打包工具,提供了完整的解决方案。本节将深入讲解如何为SPA项目配置Webpack 5。
Webpack 5 核心概念
基础配置结构
// webpack.config.js
const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry: './src/index.js', // 应用入口文件
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/' // 静态资源基础路径
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
alias: {
'@': path.resolve(__dirname, 'src') // 路径别名
}
},
module: {
rules: [ /* 加载器配置 */ ]
},
plugins: [ /* 插件配置 */ ]
};
关键配置详解
1. 处理JavaScript/JSX
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { modules: false }],
'@babel/preset-react'
],
plugins: [
'@babel/plugin-transform-runtime',
'react-hot-loader/babel'
]
}
}
}
]
}
2. 处理样式文件
{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader', // 开发环境使用
{
loader: 'css-loader',
options: {
modules: {
auto: true,
localIdentName: '[name]__[local]--[hash:base64:5]'
}
}
},
'postcss-loader', // 需要配置postcss.config.js
'sass-loader'
]
}
3. 处理静态资源
{
test: /\.(png|jpe?g|gif|svg|woff2?|eot|ttf|otf)$/,
type: 'asset/resource',
generator: {
filename: 'assets/[hash][ext][query]'
}
}
开发环境优化配置
// webpack.dev.js
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
devServer: {
hot: true,
open: true,
port: 3000,
historyApiFallback: true, // 支持HTML5 History API
static: {
directory: path.join(__dirname, 'public')
},
client: {
overlay: {
errors: true,
warnings: false
}
}
},
plugins: [
new ReactRefreshWebpackPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development')
})
]
};
生产环境优化配置
// webpack.prod.js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
mode: 'production',
devtool: 'source-map',
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
compress: {
drop_console: true
}
}
}),
new CssMinimizerPlugin()
],
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash].css'
})
]
};
高级特性配置
1. 模块联邦 (Micro Frontends)
// app1/webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
plugins: [
new ModuleFederationPlugin({
name: 'app1',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/components/Button'
},
shared: ['react', 'react-dom']
})
]
// app2/webpack.config.js
plugins: [
new ModuleFederationPlugin({
name: 'app2',
remotes: {
app1: 'app1@http://localhost:3001/remoteEntry.js'
},
shared: ['react', 'react-dom']
})
]
2. 性能分析
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins: [
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: 'bundle-report.html',
openAnalyzer: false
})
]
3. PWA支持
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
plugins: [
new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true,
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024
})
]
现代优化技巧
- 持久化缓存:
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
}
};
- ESBuild 加速:
const { ESBuildMinifyPlugin } = require('esbuild-loader');
module.exports = {
optimization: {
minimizer: [
new ESBuildMinifyPlugin({
target: 'es2015'
})
]
}
};
- 动态Polyfill:
module.exports = {
entry: ['core-js/stable', 'regenerator-runtime/runtime', './src/index.js']
};
完整配置示例
// webpack.common.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
favicon: './public/favicon.ico'
})
]
};
// webpack.config.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = (env, argv) => {
const isProduction = argv.mode === 'production';
return merge(common, isProduction
? require('./webpack.prod.js')
: require('./webpack.dev.js')
);
};
最佳实践建议
-
配置分离:
- 基础配置 (webpack.common.js)
- 开发配置 (webpack.dev.js)
- 生产配置 (webpack.prod.js)
-
环境变量管理:
const dotenv = require('dotenv-webpack'); plugins: [ new dotenv({ path: `./.env.${process.env.NODE_ENV}` }) ] -
构建速度优化:
- 使用 thread-loader 并行处理
- 缩小 loader 作用范围 (include/exclude)
- 启用持久化缓存
-
输出优化:
output: { filename: 'js/[name].[contenthash:8].js', chunkFilename: 'js/[name].[contenthash:8].chunk.js', assetModuleFilename: 'assets/[hash][ext][query]' }
通过以上配置,你已经可以构建一个现代化的SPA应用。接下来我们将学习如何实现客户端路由功能。
#前端开发
分享于 2025-03-25
上一篇:17.3 发布到 npm
下一篇:18.2 路由(History API)