【create-react-app,webpack配置超强解析】【建议收藏】😎这一次再也不怕webpack面试了

掘金作者:吃饺子不吃馅
原文链接:https://juejin.cn/post/7459740471654023220

Create React App (CRA) 是 React 官方推荐的脚手架工具,它封装了 Webpack、Babel、ESLint 等工具,提供了开箱即用的开发环境。虽然 CRA 隐藏了大部分配置细节,但在某些情况下,我们可能需要自定义 Webpack 配置。本文将详细解读 CRA 的 Webpack 配置文件,帮助你理解其工作原理和核心配置。

1. 配置文件概览

我们先用create-react-app创建一个项目,然后npm run eject,可以看到webpack的配置文件webpack.config.js

CRA 的 Webpack 配置它是一个函数,接收 webpackEnv 参数(表示当前环境,如 developmentproduction),并返回一个 Webpack 配置对象。

配置文件的主要部分包括:

  • 入口和输出配置
  • 模块解析规则
  • 插件配置
  • 优化配置
  • 开发和生产环境的差异处理
  • 缓存配置

2. 核心配置解析

2.1 入口和输出配置

入口(Entry)

1
javascript 代码解读复制代码entry: paths.appIndexJs,
  • paths.appIndexJs 是应用的入口文件,通常是 src/index.jssrc/index.tsx

输出(Output)

1
2
3
4
5
6
7
8
9
10
11
javascript 代码解读复制代码output: {
path: paths.appBuild, // 构建输出目录
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js' // 生产环境使用哈希文件名
: isEnvDevelopment && 'static/js/bundle.js', // 开发环境使用固定文件名
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js' // 异步 chunk 文件名
: isEnvDevelopment && 'static/js/[name].chunk.js',
assetModuleFilename: 'static/media/[name].[hash][ext]', // 静态资源文件名
publicPath: paths.publicUrlOrPath, // 公共路径
}
  • path:构建输出的目录,通常是 build
  • filename:主 bundle 的文件名,生产环境使用哈希值以支持缓存。
  • chunkFilename:异步加载的 chunk 文件名。
  • assetModuleFilename:静态资源(如图片、字体)的输出路径和文件名。
  • publicPath:静态资源的公共路径,通常与 homepage 配置相关。

2.2 模块解析规则【loader】

模块解析(resolve)

源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
javascript 代码解读复制代码resolve: {
modules: ['node_modules', paths.appNodeModules], // 模块查找路径
extensions: paths.moduleFileExtensions.map(ext => `.${ext}`), // 支持的扩展名
alias: {
'react-native': 'react-native-web', // 支持 React Native Web
...(modules.webpackAliases || {}), // 自定义别名
},
plugins: [
new ModuleScopePlugin(paths.appSrc, [ // 限制模块导入范围
paths.appPackageJson,
reactRefreshRuntimeEntry,
reactRefreshWebpackPluginRuntimeEntry,
babelRuntimeEntry,
babelRuntimeEntryHelpers,
babelRuntimeRegenerator,
]),
],
}
  • modules:模块查找路径,优先从 node_modules 和项目根目录的 node_modules 中查找。
  • extensions:支持的扩展名,如 .js.jsx.ts.tsx
  • alias:路径别名,支持 React Native Web 和自定义别名。
  • ModuleScopePlugin:限制模块导入范围,防止从 src 目录外导入模块。
    • paths.appPackageJson:项目的 package.json 文件。
    • reactRefreshRuntimeEntry:React 热更新的运行时入口文件。
    • reactRefreshWebpackPluginRuntimeEntry:React 热更新插件的运行时入口文件。
    • babelRuntimeEntry:Babel 运行时的入口文件。
    • babelRuntimeEntryHelpers:Babel 运行时的辅助函数文件。
    • babelRuntimeRegenerator:Babel 运行时的 regenerator 文件。

模块规则(Module Rules)

源码如下:

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
javascript 代码解读复制代码    module: {
strictExportPresence: true,
rules: `[`
shouldUseSourceMap && {
enforce: 'pre',
exclude: /@babel(?:\/|\\{1,2})runtime/,
test: /\.(js|mjs|jsx|ts|tsx|css)$/,
loader: require.resolve('source-map-loader'),
},
{
oneOf: [
{
test: [/\.avif$/],
type: 'asset',
mimetype: 'image/avif',
parser: {
dataUrlCondition: {
maxSize: imageInlineSizeLimit,
},
},
},
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: imageInlineSizeLimit,
},
},
},
{
test: /\.svg$/,
use: [
{
loader: require.resolve('@svgr/webpack'),
options: {
prettier: false,
svgo: false,
svgoConfig: {
plugins: [{ removeViewBox: false }],
},
titleProp: true,
ref: true,
},
},
{
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash].[ext]',
},
},
],
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
},
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
presets: [
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
},
],
],

plugins: [
isEnvDevelopment &&
shouldUseReactRefresh &&
require.resolve('react-refresh/babel'),
[require.resolve('babel-plugin-transform-antd-resize-table'), { optionName: path.resolve(__dirname, '../src/components/table') }]
].filter(Boolean),
cacheDirectory: true,
cacheCompression: false,
compact: isEnvProduction,
},
},
{
test: /\.(js|mjs)$/,
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
sourceMaps: shouldUseSourceMap,
inputSourceMap: shouldUseSourceMap,
},
},
{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
mode: 'icss',
},
}),
sideEffects: true,
},
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
mode: 'local',
getLocalIdent: getCSSModuleLocalIdent,
},
}),
},
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
mode: 'icss',
},
},
'sass-loader'
),
sideEffects: true,
},
{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
mode: 'local',
getLocalIdent: getCSSModuleLocalIdent,
},
},
'sass-loader'
),
},
{
exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
type: 'asset/resource',
},
],
},
].filter(Boolean),
},

module 是 Webpack 配置的核心部分,用于定义如何处理不同类型的文件(模块)。它包含两个主要属性:

  • strictExportPresence:设置为 true,表示如果模块导出不存在,Webpack 会抛出错误。
  • rules:定义了一系列规则(rules),每个规则指定了如何处理特定类型的文件。

接下来一个一个分析其作用


处理 Source Maps

1
2
3
4
5
6
javascript 代码解读复制代码shouldUseSourceMap && {
enforce: 'pre',
exclude: /@babel(?:\/|\\{1,2})runtime/,
test: /\.(js|mjs|jsx|ts|tsx|css)$/,
loader: require.resolve('source-map-loader'),
}
  • 作用:在开发环境中,使用 source-map-loader 加载 Source Maps,以便在调试时能够定位到源代码。
  • enforce: 'pre':确保该 loader 在其他 loader 之前执行。
  • exclude:排除 @babel/runtime 中的文件,避免重复处理。
  • test:匹配 JavaScript、TypeScript 和 CSS 文件。

oneOf 规则

oneOf 是 Webpack 的一种优化机制,它会按顺序遍历规则列表,直到找到匹配的规则。如果没有匹配的规则,则使用最后的 file-loader


处理 AVIF 图片

1
2
3
4
5
6
7
8
9
10
javascript 代码解读复制代码{
test: [/\.avif$/],
type: 'asset',
mimetype: 'image/avif',
parser: {
dataUrlCondition: {
maxSize: imageInlineSizeLimit,
},
},
}
  • 作用:处理 AVIF 格式的图片。
  • type: 'asset':将文件作为资源处理,小于 imageInlineSizeLimit 的图片会转为 Base64 编码。
  • mimetype:指定文件的 MIME 类型。

处理常见图片格式

1
2
3
4
5
6
7
8
9
javascript 代码解读复制代码{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: imageInlineSizeLimit,
},
},
}
  • 作用:处理 BMP、GIF、JPEG、PNG 等常见图片格式。
  • type: 'asset':将文件作为资源处理,小于 imageInlineSizeLimit 的图片会转为 Base64 编码。

处理 SVG 图片

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
javascript 代码解读复制代码{
test: /\.svg$/,
use: [
{
loader: require.resolve('@svgr/webpack'),
options: {
prettier: false,
svgo: false,
svgoConfig: {
plugins: [{ removeViewBox: false }],
},
titleProp: true,
ref: true,
},
},
{
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash].[ext]',
},
},
],
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
}
  • 作用:处理 SVG 图片,并使用 @svgr/webpack 将其转换为 React 组件。
  • @svgr/webpack:将 SVG 文件转换为 React 组件。
  • file-loader:处理 SVG 文件的静态资源路径。

处理应用代码(JavaScript/TypeScript)

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
javascript 代码解读复制代码{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve('babel-preset-react-app/webpack-overrides'),
presets: [
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
},
],
],
plugins: [
isEnvDevelopment &&
shouldUseReactRefresh &&
require.resolve('react-refresh/babel'),
[require.resolve('babel-plugin-transform-antd-resize-table'), { optionName: path.resolve(__dirname, '../src/components/table') }]
].filter(Boolean),
cacheDirectory: true,
cacheCompression: false,
compact: isEnvProduction,
},
}
  • 作用:使用 Babel 编译应用代码(JavaScript 和 TypeScript)。
  • include:仅处理 src/ 目录下的文件。
  • babel-loader:使用 Babel 编译代码。
  • presets:使用 babel-preset-react-app 预设,支持 React 和 TypeScript。
  • plugins
    • react-refresh/babel:在开发环境中启用 React 热更新。
    • babel-plugin-transform-antd-resize-table:自定义插件,用于处理 Ant Design 表格组件的调整大小功能。
  • cacheDirectory:启用 Babel 缓存,加快构建速度。

处理第三方库代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
javascript 代码解读复制代码{
test: /\.(js|mjs)$/,
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
babelrc: false,
configFile: false,
compact: false,
presets: [
[
require.resolve('babel-preset-react-app/dependencies'),
{ helpers: true },
],
],
cacheDirectory: true,
cacheCompression: false,
sourceMaps: shouldUseSourceMap,
inputSourceMap: shouldUseSourceMap,
},
}
  • 作用:使用 Babel 编译第三方库代码。
  • exclude:排除 @babel/runtime 中的文件。
  • presets:使用 babel-preset-react-app/dependencies 预设,仅编译标准的 ES 特性。

处理 CSS 文件

1
2
3
4
5
6
7
8
9
10
11
12
javascript 代码解读复制代码{
test: cssRegex,
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
modules: {
mode: 'icss',
},
}),
sideEffects: true,
}
  • 作用:处理普通的 CSS 文件。
  • getStyleLoaders:返回一组 loader,包括 style-loadercss-loaderpostcss-loader
  • modules.mode: 'icss':启用 ICSS(Interoperable CSS)模式。

处理 CSS Modules

1
2
3
4
5
6
7
8
9
10
11
javascript 代码解读复制代码{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
modules: {
mode: 'local',
getLocalIdent: getCSSModuleLocalIdent, // 使用 CRA 的类名生成规则
},
}),
}
  • 作用:处理 CSS Modules 文件。
  • modules.mode: 'local':启用 CSS Modules 的局部作用域。
  • getLocalIdent: 使用 CRA 的类名生成规则

处理 Sass 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
javascript 代码解读复制代码{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
modules: {
mode: 'icss',
},
},
'sass-loader'
),
sideEffects: true,
}
  • 作用:处理普通的 Sass 文件。
  • sass-loader:将 Sass 编译为 CSS。

处理 Sass Modules

1
2
3
4
5
6
7
8
9
10
11
12
13
14
javascript 代码解读复制代码{
test: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
modules: {
mode: 'local',
getLocalIdent: getCSSModuleLocalIdent,
},
},
'sass-loader'
),
}
  • 作用:处理 Sass Modules 文件。
  • modules.mode: 'local':启用 CSS Modules 的局部作用域。

处理其他文件

1
2
3
4
javascript 代码解读复制代码{
exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
type: 'asset/resource',
}
  • 作用:处理其他类型的文件(如图片、字体等)。
  • type: 'asset/resource':将文件作为静态资源处理,并输出到指定目录。

2.3 插件配置【plugin】

源码如下:

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
javascript 代码解读复制代码plugins: [
new HtmlWebpackPlugin({ // 生成 HTML 文件
inject: true,
template: paths.appHtml,
minify: isEnvProduction ? { // 生产环境压缩 HTML
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
} : undefined,
}),
isEnvProduction &&
new MiniCssExtractPlugin({ // 提取 CSS 文件
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
}),
new webpack.DefinePlugin(env.stringified), // 注入环境变量
isEnvDevelopment &&
shouldUseReactRefresh &&
new ReactRefreshWebpackPlugin({ // 支持 React 热更新
overlay: false,
}),
isEnvProduction &&
new WorkboxWebpackPlugin.InjectManifest({ // 生成 Service Worker
swSrc,
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
}),
useTypeScript &&
new ForkTsCheckerWebpackPlugin({ // TypeScript 类型检查
async: isEnvDevelopment,
typescript: {
typescriptPath: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
configOverwrite: {
compilerOptions: {
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
skipLibCheck: true,
incremental: true,
tsBuildInfoFile: paths.appTsBuildInfoFile,
},
},
},
}),
!disableESLintPlugin &&
new ESLintPlugin({ // ESLint 检查
extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
context: paths.appSrc,
cache: true,
}),
].filter(Boolean);

HtmlWebpackPlugin

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
javascript 代码解读复制代码new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
template: paths.appHtml,
},
isEnvProduction
? {
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}
: undefined
)
)
  • 作用:生成 index.html 文件,并自动注入打包后的 JavaScript 和 CSS 文件。
  • 配置
    • inject: true:自动将生成的资源文件注入到 HTML 中。
    • template: paths.appHtml:使用指定的 HTML 模板文件(通常是 public/index.html)。
    • minify:在生产环境中启用 HTML 压缩,移除注释、空白字符等。

InlineChunkHtmlPlugin

1
2
3
javascript 代码解读复制代码isEnvProduction &&
shouldInlineRuntimeChunk &&
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/])
  • 作用:将 Webpack 的 runtime 代码内联到 HTML 中,减少网络请求。
  • 条件:仅在生产环境且 shouldInlineRuntimeChunktrue 时启用。
  • 参数
    • HtmlWebpackPlugin:指定要操作的 HTML 插件。
    • [/runtime-.+[.]js/]:匹配需要内联的 runtime 文件。

InterpolateHtmlPlugin

1
javascript 代码解读复制代码new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw)
  • 作用:在 HTML 中插入环境变量。例如,%PUBLIC_URL% 会被替换为 publicUrlOrPath
  • 参数
    • HtmlWebpackPlugin:指定要操作的 HTML 插件。
    • env.raw:环境变量对象。

ModuleNotFoundPlugin

1
javascript 代码解读复制代码new ModuleNotFoundPlugin(paths.appPath)
  • 作用:在模块未找到时提供更友好的错误提示,帮助开发者快速定位问题。
  • 参数
    • paths.appPath:项目的根目录。

DefinePlugin

1
javascript 代码解读复制代码new webpack.DefinePlugin(env.stringified)
  • 作用:在编译时将环境变量注入到代码中。例如,process.env.NODE_ENV 会被替换为 "production""development"
  • 参数
    • env.stringified:环境变量对象,键值对会被注入到代码中。

ReactRefreshWebpackPlugin

1
2
3
4
5
javascript 代码解读复制代码isEnvDevelopment &&
shouldUseReactRefresh &&
new ReactRefreshWebpackPlugin({
overlay: false,
})
  • 作用:在开发环境中启用 React 组件的热更新(Hot Module Replacement, HMR)。
  • 条件:仅在开发环境且 shouldUseReactRefreshtrue 时启用。
  • 参数
    • overlay: false:禁用错误覆盖层。

CaseSensitivePathsPlugin

1
javascript 代码解读复制代码isEnvDevelopment && new CaseSensitivePathsPlugin()
  • 作用:在开发环境中检查文件路径的大小写敏感性,避免因路径大小写不一致导致的错误。
  • 条件:仅在开发环境中启用。

MiniCssExtractPlugin

1
2
3
4
5
javascript 代码解读复制代码isEnvProduction &&
new MiniCssExtractPlugin({
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
})
  • 作用:将 CSS 提取为单独的文件,而不是内联到 JavaScript 中。
  • 条件:仅在生产环境中启用。
  • 参数
    • filename:主 CSS 文件的输出路径和名称。
    • chunkFilename:异步加载的 CSS 文件的输出路径和名称。

WebpackManifestPlugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
javascript 代码解读复制代码new WebpackManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: paths.publicUrlOrPath,
generate: (seed, files, entrypoints) => {
const manifestFiles = files.reduce((manifest, file) => {
manifest[file.name] = file.path;
return manifest;
}, seed);
const entrypointFiles = entrypoints.main.filter(
fileName => !fileName.endsWith('.map')
);

return {
files: manifestFiles,
entrypoints: entrypointFiles,
};
},
})
  • 作用:生成资源清单文件(asset-manifest.json),记录所有打包后的资源文件及其路径。
  • 参数
    • fileName:清单文件的名称。
    • publicPath:资源的公共路径。
    • generate:自定义清单文件的生成逻辑。

IgnorePlugin

1
2
3
4
javascript 代码解读复制代码new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
})
  • 作用:忽略 moment.js 中的本地化文件,减少打包体积。
  • 参数
    • resourceRegExp:匹配要忽略的资源。
    • contextRegExp:匹配资源的上下文。

WorkboxWebpackPlugin.InjectManifest

1
2
3
4
5
6
7
8
javascript 代码解读复制代码isEnvProduction &&
fs.existsSync(swSrc) &&
new WorkboxWebpackPlugin.InjectManifest({
swSrc,
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
})
  • 作用:生成 Service Worker 文件,支持 PWA(渐进式 Web 应用)。
  • 条件:仅在生产环境且 swSrc 文件存在时启用。
  • 参数
    • swSrc:Service Worker 的源文件路径。
    • dontCacheBustURLsMatching:匹配不需要缓存的文件。
    • exclude:排除不需要缓存的文件。
    • maximumFileSizeToCacheInBytes:设置缓存文件的最大大小。

ForkTsCheckerWebpackPlugin

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
javascript 代码解读复制代码useTypeScript &&
new ForkTsCheckerWebpackPlugin({
async: isEnvDevelopment,
typescript: {
typescriptPath: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
configOverwrite: {
compilerOptions: {
sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
skipLibCheck: true,
inlineSourceMap: false,
declarationMap: false,
noEmit: true,
incremental: true,
tsBuildInfoFile: paths.appTsBuildInfoFile,
},
},
context: paths.appPath,
diagnosticOptions: {
syntactic: true,
},
mode: 'write-references',
},
issue: {
include: [
{ file: '../**/src/**/*.{ts,tsx}' },
{ file: '**/src/**/*.{ts,tsx}' },
],
exclude: [
{ file: '**/src/**/__tests__/**' },
{ file: '**/src/**/?(*.){spec|test}.*' },
{ file: '**/src/setupProxy.*' },
{ file: '**/src/setupTests.*' },
],
},
logger: {
infrastructure: 'silent',
},
})
  • 作用:在单独的进程中检查 TypeScript 类型错误,提升构建速度。
  • 条件:仅在项目使用 TypeScript 时启用。
  • 参数
    • async: isEnvDevelopment:在开发环境中异步检查类型错误。
    • typescript:TypeScript 配置,包括路径、编译器选项等。
    • issue:指定需要检查的文件范围。
    • logger:控制日志输出。

ESLintPlugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
javascript 代码解读复制代码!disableESLintPlugin &&
new ESLintPlugin({
extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
context: paths.appSrc,
cache: true,
cacheLocation: path.resolve(
paths.appNodeModules,
'.cache/.eslintcache'
),
cwd: paths.appPath,
resolvePluginsRelativeTo: __dirname,
baseConfig: {
extends: [require.resolve('eslint-config-react-app/base')],
rules: {
...(!hasJsxRuntime && {
'react/react-in-jsx-scope': 'error',
}),
},
},
})
  • 作用:在 Webpack 构建过程中运行 ESLint,检查代码规范。
  • 条件:仅在未禁用 ESLint 时启用。
  • 参数
    • extensions:需要检查的文件扩展名。
    • formatter:指定 ESLint 的输出格式。
    • failOnError:在发现错误时是否终止构建。
    • context:指定 ESLint 的工作目录。
    • cache:启用 ESLint 缓存,提升检查速度。
    • baseConfig:指定 ESLint 的基础配置。

2.4 优化配置

代码压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
javascript 代码解读复制代码optimization: {
minimize: isEnvProduction,
minimizer: [
new TerserPlugin({ // 压缩 JavaScript
terserOptions: {
parse: { ecma: 8 },
compress: { ecma: 5, warnings: false, comparisons: false, inline: 2 },
mangle: { safari10: true },
output: { ecma: 5, comments: false, ascii_only: true },
},
}),
new CssMinimizerPlugin(), // 压缩 CSS
],
}
  • TerserPlugin:压缩 JavaScript 代码。
  • CssMinimizerPlugin:压缩 CSS 代码。

代码分割

1
2
3
4
5
6
7
javascript 代码解读复制代码splitChunks: {
chunks: 'all', // 对所有 chunk 进行代码分割
name: false, // 不指定 chunk 名称
},
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`, // 提取 runtime 代码
},
  • splitChunks:将公共代码提取到单独的 chunk 中。
  • runtimeChunk:提取 Webpack 的 runtime 代码,减少重复加载。

2.5 缓存配置

1
2
3
4
5
6
7
8
9
10
11
12
13
js 代码解读复制代码    cache: {
type: 'filesystem',
version: createEnvironmentHash(env.raw),
cacheDirectory: paths.appWebpackCache,
store: 'pack',
buildDependencies: {
defaultWebpack: ['webpack/lib/'],
config: [__filename],
tsconfig: [paths.appTsConfig, paths.appJsConfig].filter(f =>
fs.existsSync(f)
),
},
},

缓存的作用

Webpack 的缓存机制可以显著提升构建性能,尤其是在大型项目中。它的核心作用包括:

  • 减少重复编译:通过缓存已编译的模块,避免重复处理未变化的文件。
  • 加快构建速度:在后续构建中直接使用缓存结果,减少构建时间。
  • 支持增量构建:只重新编译变化的文件,而不是整个项目。

配置详解

type: 'filesystem'

1
javascript 代码解读复制代码type: 'filesystem'
  • 作用:启用文件系统缓存,将缓存数据存储到磁盘中。
  • 说明
    • Webpack 5 引入了持久化缓存功能,支持将缓存数据存储到文件系统中。
    • 与内存缓存(memory)相比,文件系统缓存在重启构建后仍然有效。

version: createEnvironmentHash(env.raw)

1
javascript 代码解读复制代码version: createEnvironmentHash(env.raw)
  • 作用:为缓存生成一个唯一的版本标识符。
  • 说明
    • createEnvironmentHash 是一个工具函数,用于根据环境变量(env.raw)生成哈希值。
    • 当环境变量发生变化时,缓存版本会更新,确保缓存数据的有效性。

cacheDirectory: paths.appWebpackCache

1
javascript 代码解读复制代码cacheDirectory: paths.appWebpackCache
  • 作用:指定缓存文件的存储目录。
  • 说明
    • paths.appWebpackCache 是缓存目录的路径,通常是 node_modules/.cache/webpack
    • 缓存文件会存储在该目录下,以便后续构建时复用。

store: 'pack'

1
javascript 代码解读复制代码store: 'pack'
  • 作用:指定缓存存储的方式。
  • 说明
    • pack 是 Webpack 5 中的一种缓存存储方式,它会将缓存数据打包成一个文件。
    • 这种方式可以减少文件数量,提升缓存读写效率。

buildDependencies

1
2
3
4
5
6
7
javascript 代码解读复制代码buildDependencies: {
defaultWebpack: ['webpack/lib/'],
config: [__filename],
tsconfig: [paths.appTsConfig, paths.appJsConfig].filter(f =>
fs.existsSync(f)
),
}
  • 作用:指定构建依赖,当这些依赖发生变化时,缓存会失效。
  • 说明
    • defaultWebpack: ['webpack/lib/']:将 Webpack 的核心库作为构建依赖。如果 Webpack 版本发生变化,缓存会失效。
    • config: [__filename]:将当前配置文件(webpack.config.js)作为构建依赖。如果配置文件发生变化,缓存会失效。
    • tsconfig: [paths.appTsConfig, paths.appJsConfig]:将 TypeScript 配置文件(tsconfig.json)和 JavaScript 配置文件(jsconfig.json)作为构建依赖。如果这些文件发生变化,缓存会失效。