const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const EslintWebpackPlugin = require('eslint-webpack-plugin')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const WebpackBar = require('webpackbar')
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
const isProduction = process.env.NODE_ENV === 'production'
module.exports = {
mode: isProduction ? 'production' : 'development',
cache: false,
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
alias: {
'@src': path.resolve(__dirname, '../src'),
'@app': path.resolve(__dirname, '../src/app'),
'@assets': path.resolve(__dirname, '../src/assets'),
'@common': path.resolve(__dirname, '../src/common'),
'@components': path.resolve(__dirname, '../src/components'),
'@pages': path.resolve(__dirname, '../src/pages'),
'@language': path.resolve(__dirname, '../src/language'),
'@redux': path.resolve(__dirname, '../src/redux'),
'@router': path.resolve(__dirname, '../src/router')
}
},
entry: './src/index.tsx',
output: {
path: isProduction ? path.resolve(__dirname, '../desktop/build') : undefined,
filename: 'js/[name].[contenthash:8].js',
chunkFilename: 'js/[name].[contenthash:8].chunk.js',
clean: true,
publicPath: isProduction ? './' : '/'
},
module: {
rules: [
{
oneOf: [
{
test: /\.(css|less)$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['postcss-preset-env']
}
}
},
{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true
}
}
},
{
loader: 'style-resources-loader',
options: {
patterns: path.resolve(__dirname, '../src/assets/style/variables.less')
}
}
]
},
{
test: /\.(png|jpe?g|gif|webp|svg|ico)$/,
type: 'asset',
exclude: [path.resolve(__dirname, '../src/svgIcons/icons')],
parser: {
dataUrlCondition: {
maxSize: 8 * 1024
}
},
generator: {
filename: 'image/[name].[hash:8][ext]'
}
},
{
test: /\.(woff2?|eot|ttf|otf|mp3|mp4|avi|mkv)$/,
type: 'asset/resource',
generator: {
filename: 'media/[name].[hash:8][ext]'
}
},
{
test: /\.svg$/,
include: [path.resolve(__dirname, '../src/svgIcons/icons')],
use: {
loader: 'svg-sprite-loader',
options: {
symbolId: 'svg-[name]',
},
},
},
{
test: /\.(js|ts)x?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
// 按需导入core-js
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: 3
}
],
'@babel/preset-react',
'@babel/preset-typescript'
],
cacheDirectory: true,
cacheCompression: false,
plugins: [
!isProduction && 'react-refresh/babel',
'@babel/plugin-transform-runtime',
// 按需导入antd
[
'import',
{
libraryName: 'antd',
libraryDirectory: 'es',
style: true
}
]
].filter(Boolean)
}
}
]
},
]
}
]
},
plugins: [
new EslintWebpackPlugin({
context: path.resolve(__dirname, '../src'),
cache: true
}),
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html',
}),
new WebpackBar(),
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../desktop/build'),
globOptions: {
ignore: ['**/index.html']
}
}
]
}),
new SpriteLoaderPlugin({ plainSprite: true }),
isProduction && new MiniCssExtractPlugin({
filename: 'style/[name].[contenthash:8].css',
chunkFilename: 'style/[name].[contenthash:8].chunk.css'
}),
!isProduction && new ReactRefreshWebpackPlugin()
].filter(Boolean),
optimization: {
minimize: isProduction,
minimizer: [
new CssMinimizerWebpackPlugin(),
new TerserWebpackPlugin(),
],
splitChunks: {
chunks: 'all',
name: false,
cacheGroups: {
reactBase: {
name: 'reactBase',
test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom)[\\/]/,
priority: 10,
chunks: 'all'
},
antdBase: {
name: 'antdBase',
test: /[\\/]node_modules[\\/](antd|@ant-design)[\\/]/,
priority: 9,
chunks: 'all'
},
common: {
name: 'common',
minChunks: 2,
priority: 5,
chunks: 'all'
}
}
},
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
}
},
devServer: {
historyApiFallback: true,
compress: true,
host: '0.0.0.0',
port: 8000,
hot: true
},
devtool: isProduction ? 'source-map' : 'eval-cheap-module-source-map',
performance: {
hints: false,
maxEntrypointSize: 512000,
maxAssetSize: 512000
}
}
这个 Webpack 配置文件是一个典型的现代前端项目配置,主要用于构建和优化一个基于 React 的 TypeScript 项目。下面我将逐个解释这些配置在项目中的作用:
基础配置
- **`mode`**:
- 根据环境变量 `NODE_ENV` 的值设置 Webpack 的模式为 `production` 或 `development`。这会影响 Webpack 的默认行为,例如在生产模式下会压缩代码。
- **`cache`**:
- 设置为 `false`,表示禁用 Webpack 的缓存。这在开发过程中通常不需要,但在生产环境中可以提高构建速度。
- **`resolve`**:
-
**`extensions`**: 自动解析指定的扩展名,这样在导入模块时可以省略扩展名。
-
**`alias`**: 设置路径别名,方便在项目中快速引用特定的目录,例如 `@src` 指向 `src` 目录。
- **`entry`**:
- 指定项目的主要入口文件,这里是 `./src/index.tsx`。
- **`output`**:
-
**`path`**: 输出文件的目录,生产模式下输出到 `../desktop/build`。
-
**`filename`**: 输出的文件名,使用 `[contenthash:8]` 生成基于内容的哈希值,用于缓存控制。
-
**`chunkFilename`**: 代码分割后的文件名。
-
**`clean`**: 在每次构建前清理输出目录。
-
**`publicPath`**: 设置资源的公共路径,生产模式下为 `./`,开发模式下为 `/`。
模块加载器(`module.rules`)
- **CSS 和 LESS 文件**:
-
使用 `MiniCssExtractPlugin.loader` 或 `style-loader` 将 CSS 提取为单独的文件(生产模式)或直接注入到页面中(开发模式)。
-
使用 `css-loader` 和 `less-loader` 解析 CSS 和 LESS 文件。
-
使用 `postcss-loader` 进行 CSS 预处理,例如自动添加浏览器前缀。
-
使用 `style-resources-loader` 全局注入 LESS 变量文件。
- **图片和字体文件**:
-
使用 `asset` 类型处理图片和字体文件,根据文件大小决定是否转换为 Base64(小于 8KB 的文件)。
-
设置输出路径为 `image` 或 `media` 目录。
- **SVG 文件**:
- 对于 `src/svgIcons/icons` 目录下的 SVG 文件,使用 `svg-sprite-loader` 将 SVG 文件打包为一个精灵图,方便在项目中使用。
- **JavaScript 和 TypeScript 文件**:
-
使用 `babel-loader` 转译 JavaScript 和 TypeScript 文件,支持 ES6+、React 和 TypeScript。
-
使用 `@babel/preset-env` 按需导入 `core-js`,确保兼容性。
-
使用 `@babel/preset-react` 支持 JSX 语法。
-
使用 `@babel/preset-typescript` 支持 TypeScript。
-
在开发模式下启用 `react-refresh/babel` 插件,支持热更新。
插件(`plugins`)
- **`EslintWebpackPlugin`**:
- 在构建过程中运行 ESLint,确保代码风格一致并发现潜在问题。
- **`HtmlWebpackPlugin`**:
- 自动生成 HTML 文件,并将打包后的 JS 和 CSS 文件注入到 HTML 中。
- **`WebpackBar`**:
- 在控制台显示一个进度条,方便观察构建进度。
- **`CopyWebpackPlugin`**:
- 将 `public` 目录下的文件(除了 `index.html`)复制到输出目录。
- **`SpriteLoaderPlugin`**:
- 用于处理 SVG 精灵图。
- **`MiniCssExtractPlugin`**:
- 在生产模式下提取 CSS 为单独的文件。
- **`ReactRefreshWebpackPlugin`**:
- 在开发模式下启用 React 热更新。
优化(`optimization`)
- **`minimize`**:
- 在生产模式下启用代码压缩。
- **`minimizer`**:
- 使用 `CssMinimizerWebpackPlugin` 和 `TerserWebpackPlugin` 分别压缩 CSS 和 JavaScript 文件。
- **`splitChunks`**:
- 配置代码分割,将公共模块(如 React、Ant Design 等)提取到单独的文件中,减少重复代码,提高缓存效率。
- **`runtimeChunk`**:
- 将 Webpack 的运行时代码提取到单独的文件中,提高缓存效率。
开发服务器(`devServer`)
- 配置开发服务器,支持热更新、压缩、自定义主机和端口等。
源码映射(`devtool`)
- 在生产模式下使用 `source-map`,在开发模式下使用 `eval-cheap-module-source-map`,方便调试。
性能(`performance`)
- 禁用性能提示,设置最大入口点和资源大小,避免构建过程中出现警告。
这个配置文件涵盖了从开发到生产环境的各种需求,确保项目在开发时具有良好的开发体验,在生产环境中具有高效的构建和优化。