gitHub链接:github.com/facebook/cr...
解析 以下是 Create React App Webpack 配置的核心模块解析(基于 v4.0.3 版本):
一、基础架构
-
环境判断
通过
webpackEnv
参数区分开发/生产环境:inijavascript const isEnvDevelopment = webpackEnv === 'development'; const isEnvProduction = webpackEnv === 'production';
-
入口配置
动态注入热更新客户端和业务代码:
javascriptjavascript entry: [ isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'), paths.appIndexJs // 主入口 src/index.js ].filter(Boolean)
二、核心优化策略
-
输出策略
arduinojavascript output: { filename: isEnvProduction ? 'static/js/[name].[contenthash:8].js' // 生产环境哈希命名 : 'static/js/bundle.js', // 开发环境固定命名 chunkFilename: isEnvProduction ? 'static/js/[name].[contenthash:8].chunk.js' : 'static/js/[name].chunk.js', publicPath: paths.publicUrlOrPath // 从 package.json 的 homepage 推导 }
-
分包策略
自动拆分公共模块和运行时文件:
yamljavascript optimization: { splitChunks: { chunks: 'all', name: false }, // 自动拆分 vendor runtimeChunk: { name: entrypoint => `runtime-${entrypoint.name}` } }
三、关键模块处理规则
-
JS/TS 处理
-
使用
babel-loader
配合babel-preset-react-app
-
通过
include: paths.appSrc
限制编译范围 -
开发环境启用缓存加速构建:
vbnetjavascript cacheDirectory: true, cacheCompression: false
-
-
样式处理
开发/生产环境差异化配置:
inijavascript getStyleLoaders = (cssOptions, preProcessor) => { const loaders = [ isEnvDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, { loader: 'css-loader', options: cssOptions }, { loader: 'postcss-loader', options: { // 自动添加浏览器前缀 postcssOptions: { plugins: [['postcss-preset-env', { flexbox: 'no-2009' }]] } } } ] }
-
静态资源处理
javascriptjavascript { test: [/.bmp$/, /.gif$/, /.jpe?g$/, /.png$/], type: 'asset', parser: { dataUrlCondition: { maxSize: 10*1024 } } // <10KB 内联 }
四、核心插件机制18
-
HTML 生成
yamljavascript new HtmlWebpackPlugin({ template: paths.appHtml, // 使用 public/index.html 模板 minify: isEnvProduction ? { // 生产环境压缩 removeComments: true, collapseWhitespace: true, minifyJS: true } : undefined })
-
环境变量注入
arduinojavascript new webpack.DefinePlugin(env.stringified) // 注入 process.env
-
开发工具链
CaseSensitivePathsPlugin
:强制区分文件名大小写WatchMissingNodeModulesPlugin
:监控模块安装状态ForkTsCheckerWebpackPlugin
:独立线程进行 TS 类型检查
五、安全策略
-
模块作用域限制
scssjavascript new ModuleScopePlugin(paths.appSrc) // 禁止导入 src 外部的源码
-
源码保护
通过
@remove-on-eject
注释保护关键配置:sqljavascript // @remove-on-eject-begin cacheIdentifier: getCacheIdentifier(...) // eject 后移除 // @remove-on-eject-end
实现了开箱即用的最佳实践:开发环境优化构建速度(热更新、缓存),生产环境优化输出质量(代码分割、哈希命名)。
源码:
//
// @remove-on-eject-begin
/* 版权声明 */
// @remove-on-eject-end
'use strict';
// 核心模块引入
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const resolve = require('resolve');
// 插件模块
const PnpWebpackPlugin = require('pnp-webpack-plugin'); // PnP模块解析
const HtmlWebpackPlugin = require('html-webpack-plugin'); // HTML生成
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); // 路径大小写敏感
const TerserPlugin = require('terser-webpack-plugin'); // JS压缩
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // CSS提取
// ------------------------ 基础配置 ------------------------
const paths = require('./paths'); // 路径配置文件
const modules = require('./modules'); // 模块配置
const getClientEnvironment = require('./env'); // 环境变量
// ======================= 主配置函数 =======================
module.exports = function(webpackEnv) {
// --------------- 环境判断 ---------------
const isEnvDevelopment = webpackEnv === 'development'; // 开发模式
const isEnvProduction = webpackEnv === 'production'; // 生产模式
// --------------- 样式加载器工厂函数 ---------------
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
// 开发环境用style-loader注入<style>标签
isEnvDevelopment && 'style-loader',
// 生产环境提取CSS为独立文件
isEnvProduction && {
loader: MiniCssExtractPlugin.loader,
options: { publicPath: '../../' } // 路径调整
},
{
loader: 'css-loader', // 解析CSS导入
options: cssOptions
},
{
loader: 'postcss-loader', // 自动添加浏览器前缀
options: {
postcssOptions: {
plugins: [
['postcss-preset-env', { flexbox: 'no-2009' }] // 兼容旧版flexbox
]
}
}
}
].filter(Boolean); // 过滤空值
// 预处理器(如Sass)
if (preProcessor) {
loaders.push(
'resolve-url-loader', // 解析相对路径
{
loader: preProcessor,
options: { sourceMap: true } // 启用sourcemap
}
);
}
return loaders;
};
// ================ 返回完整配置对象 ================
return {
// 模式配置
mode: isEnvProduction ? 'production' : 'development',
// 生产环境构建失败时终止
bail: isEnvProduction,
// 开发工具配置
devtool: isEnvProduction
? 'source-map' // 生产环境完整sourcemap
: 'cheap-module-source-map', // 开发环境快速sourcemap
// --------------- 入口配置 ---------------
entry: [
// 开发环境注入热更新客户端
isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appIndexJs // 主入口文件
].filter(Boolean),
// --------------- 输出配置 ---------------
output: {
path: isEnvProduction ? paths.appBuild : undefined, // 生产输出目录
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js' // 生产带哈希文件名
: 'static/js/bundle.js', // 开发固定文件名
chunkFilename: isEnvProduction // 异步块文件名
? 'static/js/[name].[contenthash:8].chunk.js'
: 'static/js/[name].chunk.js',
publicPath: paths.publicUrlOrPath, // 公共资源路径
globalObject: 'this' // 兼容Web Worker
},
// --------------- 优化配置 ---------------
optimization: {
minimize: isEnvProduction, // 生产环境启用压缩
minimizer: [
// JS压缩器(生产环境)
new TerserPlugin({
terserOptions: {
parse: { ecma: 8 }, // 支持ES2017解析
compress: {
ecma: 5,
comparisons: false, // 禁用对比优化
inline: 2 // 函数内联级别
}
}
}),
// CSS压缩(生产环境)
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safePostCssParser,
map: shouldUseSourceMap // 是否生成sourcemap
}
})
],
// 代码分割策略
splitChunks: {
chunks: 'all', // 同步/异步代码均分割
name: false // 自动生成chunk名称
},
// 分离Webpack运行时文件
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
}
},
// --------------- 模块解析配置 ---------------
resolve: {
modules: ['node_modules', paths.appNodeModules], // 模块搜索路径
extensions: ['.js', '.jsx', '.ts', '.tsx'], // 自动解析扩展名
plugins: [
PnpWebpackPlugin, // 支持Yarn PnP
// 限制src目录外部的模块导入
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson])
]
},
// --------------- 模块处理规则 ---------------
module: {
rules: [
// JS/TS规则
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: 'babel-loader', // 使用Babel转译
options: {
presets: ['react-app'], // CRA预设
cacheDirectory: true, // 启用缓存
cacheCompression: false // 禁用缓存压缩
}
},
// CSS规则
{
test: cssRegex,
use: getStyleLoaders({ importLoaders: 1 })
},
// 图片资源处理
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
type: 'asset', // Webpack5内置资源模块
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 10KB以下转base64
}
}
}
]
},
// --------------- 插件配置 ---------------
plugins: [
// 生成HTML文件
new HtmlWebpackPlugin({
template: paths.appHtml, // HTML模板
minify: isEnvProduction && { // 生产环境压缩
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true
}
}),
// 注入环境变量
new webpack.DefinePlugin(getClientEnvironment().stringified),
// 开发环境插件
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(), // HMR
isEnvDevelopment && new CaseSensitivePathsPlugin(), // 路径大小写敏感
// 生产环境插件
isEnvProduction &&
new MiniCssExtractPlugin({ // 提取CSS
filename: 'static/css/[name].[contenthash:8].css',
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css'
})
].filter(Boolean)
};
};