В основе Create React App лежит абстракция над инструментами сборки,
где основную роль играет Webpack, Babel, ESLint и набор вспомогательных
плагинов. Вся конфигурация инкапсулирована внутри пакета
react-scripts, что избавляет от необходимости вручную
настраивать окружение.
Внутри node_modules/react-scripts находится полностью
готовая система сборки, включающая:
Основная идея заключается в том, что пользователь не взаимодействует с Webpack напрямую, а работает через ограниченный набор CLI-команд:
react-scripts startreact-scripts buildreact-scripts testreact-scripts ejectКоманда eject выполняет извлечение всей внутренней
конфигурации CRA в проект пользователя. После выполнения происходит
копирование всех скрытых конфигурационных файлов в корень проекта.
Ключевые последствия:
react-scriptsПосле eject проект фактически превращается в классическое React-приложение с полностью ручной сборкой.
При запуске:
npm run eject
или
yarn eject
выполняются следующие действия:
Копируются конфигурации Webpack:
config/webpack.config.jsconfig/webpackDevServer.config.jsГенерируются окружения сборки:
config/env.js.env-обработка остается, но расширяетсяПереносятся скрипты сборки:
scripts/build.jsscripts/start.jsscripts/test.jsДобавляются зависимости в package.json, которые
ранее были скрыты внутри react-scripts
Удаляется react-scripts как единая
абстракция
После извлечения проект обычно принимает следующий вид:
project/
config/
webpack.config.js
webpackDevServer.config.js
paths.js
modules.js
env.js
scripts/
start.js
build.js
test.js
src/
package.json
Каждый файл конфигурации становится доступным для прямого редактирования, что радикально меняет модель разработки.
Файл webpack.config.js становится центральным элементом
всей сборки. Он содержит несколько крупных секций:
Конфигурация разделяется на production и development:
development:
production:
entry: [
isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
paths.appIndexJs,
].filter(Boolean)
Здесь видно, что dev-сборка включает дополнительный клиент для hot reload.
output: {
path: paths.appBuild,
filename: 'static/js/[name].[contenthash:8].js',
publicPath: '/',
}
Используется contenthash для кеширования в production-сборке.
Основные loader-цепочки:
Babel loader:
Style loaders:
File loader:
{
test: /\.(js|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
presets: [require.resolve('babel-preset-react-app')],
cacheDirectory: true,
},
}
CRA после eject использует функцию, возвращающую конфигурацию:
module.exports = function (webpackEnv) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
Это позволяет динамически строить конфиг в зависимости от режима.
В development режиме подключается HMR:
new webpack.HotModuleReplacementPlugin()
и dev client:
react-dev-utils/webpackHotDevClient
Это обеспечивает обновление модулей без полной перезагрузки страницы.
CRA использует разные стратегии source maps:
cheap-module-source-mapsource-map (или отключён в зависимости от
флага)Это влияет на скорость сборки и качество отладки.
Конфигурация ESLint также становится явной:
.eslintrcОбработка CSS включает цепочку:
style-loader или MiniCssExtractPlugincss-loaderpostcss-loaderPostCSS конфигурация включает:
Webpack после eject включает:
optimization: {
splitChunks: {
chunks: 'all',
},
}
Это позволяет разделять vendor и application код.
Используется TerserPlugin:
MiniCssExtractPlugin выносит стили в отдельные файлы:
static/css/main.[contenthash].css
CRA использует модуль paths.js, где определяются:
Пример:
module.exports = {
appSrc: resolveApp('src'),
appBuild: resolveApp('build'),
}
После извлечения конфигурации появляются системные сложности:
Любое изменение теперь требует понимания всей цепочки сборки, а не только React-кода.
Вместо полного eject часто используют частичную кастомизацию:
Это позволяет сохранить управление CRA, не теряя обновляемость базовой системы.