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

1,093 阅读5分钟

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

1. 配置文件概览

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

image.png

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

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

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

2. 核心配置解析

2.1 入口和输出配置

入口(Entry)

entry: paths.appIndexJs,
  • paths.appIndexJs 是应用的入口文件,通常是 src/index.jssrc/index.tsx

输出(Output)

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)

源码如下:

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)

源码如下:

    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

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 图片

{
  test: [/\.avif$/],
  type: 'asset',
  mimetype: 'image/avif',
  parser: {
    dataUrlCondition: {
      maxSize: imageInlineSizeLimit,
    },
  },
}
  • 作用:处理 AVIF 格式的图片。
  • type: 'asset':将文件作为资源处理,小于 imageInlineSizeLimit 的图片会转为 Base64 编码。
  • mimetype:指定文件的 MIME 类型。

处理常见图片格式

{
  test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: imageInlineSizeLimit,
    },
  },
}
  • 作用:处理 BMP、GIF、JPEG、PNG 等常见图片格式。
  • type: 'asset':将文件作为资源处理,小于 imageInlineSizeLimit 的图片会转为 Base64 编码。

处理 SVG 图片

{
  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)

{
  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 缓存,加快构建速度。

处理第三方库代码

{
  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 文件

{
  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

{
  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 文件

{
  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

{
  test: sassModuleRegex,
  use: getStyleLoaders(
    {
      importLoaders: 3,
      sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
      modules: {
        mode: 'local',
        getLocalIdent: getCSSModuleLocalIdent,
      },
    },
    'sass-loader'
  ),
}
  • 作用:处理 Sass Modules 文件。
  • modules.mode: 'local':启用 CSS Modules 的局部作用域。

处理其他文件

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

2.3 插件配置【plugin】

源码如下:

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

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

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

InterpolateHtmlPlugin

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

ModuleNotFoundPlugin

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

DefinePlugin

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

ReactRefreshWebpackPlugin

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

CaseSensitivePathsPlugin

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

MiniCssExtractPlugin

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

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

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

WorkboxWebpackPlugin.InjectManifest

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

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

!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 优化配置

代码压缩

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 代码。

代码分割

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

2.5 缓存配置

    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'

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

version: createEnvironmentHash(env.raw)

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

cacheDirectory: paths.appWebpackCache

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

store: 'pack'

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

buildDependencies

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)作为构建依赖。如果这些文件发生变化,缓存会失效。