# 基础到高级的Webpack配置详解
# 目录
- Webpack基础概念
- 基础配置
- 加载器(Loader)配置
- 插件(Plugins)使用
- 优化构建性能
- 代码分割与懒加载
- 环境配置管理
- 模块热替换(HMR)
- 高级配置案例
- Webpack 5新特性
- 常见问题与解决方案
# Webpack基础概念
# 什么是Webpack?
Webpack是一个现代JavaScript应用程序的静态模块打包器。当Webpack处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle。
# 核心概念
- 入口(Entry): 指定webpack开始构建其内部依赖图的起点
- 输出(Output): 告诉webpack在哪里输出所创建的bundle
- 加载器(Loaders): 让webpack能够处理非JavaScript文件
- 插件(Plugins): 用于执行范围更广的任务,如打包优化、资源管理等
- 模式(Mode): 设置development, production或none来启用相应模式下的优化
# 安装与基本使用
# 安装webpack和webpack-cli
npm install webpack webpack-cli --save-dev
# 创建基本的配置文件
touch webpack.config.js
1
2
3
4
5
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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的配置有了全面的了解,从基础概念到高级用法,再到性能优化和最佳实践。随着项目的不断发展,可以根据需要逐步应用这些配置,打造出优质的前端构建流程。