Create React App 4.x 版本演进全解析:从 4.0.0 大版本到 4.0.3 维护版的变更、迁移指南与源码验证
本文以 create-react-app 仓库中的 CHANGELOG-4.x.md 为主体,完整梳理 4.x 系列四个版本(4.0.0、4.0.1、4.0.2、4.0.3)的全部变更条目、破坏性变更与迁移命令,并结合仓库内 react-scripts 的实际源码(如 config/env.js、config/webpack.config.js、scripts/utils/createJestConfig.js)逐项验证这些变更的落地方式。读完本篇,你既能掌握每个版本的完整升级路径与官方迁移命令,也能理解 Fast Refresh、新 JSX Transform、dotenv 加载顺序修正、Jest resetMocks 等核心特性在源码中的真实实现。
4.x 系列版本总览
CHANGELOG 覆盖 4.x 系列的四个版本,发布时间线与定位如下:
| 版本 | 发布日期 | 定位 |
|---|---|---|
| 4.0.3 | 2021-02-22 | 维护版:小 bug 修复与依赖更新 |
| 4.0.2 | 2021-02-03 | 维护版:小 bug 修复与文档更新 |
| 4.0.1 | 2020-11-23 | 维护版:小 bug 修复与文档更新 |
| 4.0.0 | 2020-10-23 | 大版本:Fast Refresh、React 17 支持等多项新特性 |
每个版本都附带统一的迁移方式:对于任何尚未 eject 的项目,只需精确锁定 react-scripts 版本即可升级。例如从 4.0.2 升到 4.0.3:
npm install --save --save-exact react-scripts@4.0.3
或
yarn add --exact react-scripts@4.0.3
同理,从 4.0.1 升到 4.0.2、从 4.0.0 升到 4.0.1、从 3.4.x 升到 4.0.0,命令形式一致(版本号相应替换)。CHANGELOG 中 4.0.0 的迁移部分还特别强调:升级后如果遇到错误,可能需要删除 node_modules 文件夹并用 yarn(或 npm install)重装依赖。对于曾经 eject 过的项目,官方建议的解法是:找到 eject 时的提交(以及后续修改配置的提交)将其回退、先升级,之后再视需要重新 eject;同时也有可能当初 eject 所依赖的功能现在已开箱即用。
4.0.0:Fast Refresh 与 React 17 支持的大版本
核心亮点
CHANGELOG 列出了 4.0.0 的八大亮点,逐项都有对应的源码落点:
- Fast Refresh(PR #8582):实验性 React 快速刷新,开发模式下组件编辑后秒级热替换且不丢失状态;
- React 17 支持与新 JSX Transform(PR #9645):不再强制
import React from 'react'; - TypeScript 4 支持(PR #9734):配合 TS 4.1 使用新的 JSX 编译设置;
- ESLint 7(PR #8978),并新增 Jest 与 React Testing Library 规则(PR #8963);
- Jest 26(PR #8955);
- PWA/Workbox 改进:切换到 Workbox InjectManifest 插件(PR #9205),PWA 模板移入独立仓库以便独立发布;
- Web Vitals 支持(PR #9116)。
Fast Refresh 在源码中的实现
在 config/webpack.config.js 中,开发环境通过 @pmmmwh/react-refresh-webpack-plugin 启用刷新:
isEnvDevelopment &&
shouldUseReactRefresh &&
new ReactRefreshWebpackPlugin({
overlay: false,
}),
其中 shouldUseReactRefresh = env.raw.FAST_REFRESH(同文件第 115 行),而 FAST_REFRESH 的默认逻辑在 config/env.js 中定义:
FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
也就是说 默认开启,设置 FAST_REFRESH=false 即可关闭。该变量会注入到客户端,供 webpackHotDevClient.js 判断是否启用 React Refresh 运行时。4.0.1 中修复的 "FAST_REFRESH=false 时页面不刷新" 问题,修的正是这条链路。另外 scripts/start.js 会在 React 版本低于 16.10 时打印 Fast Refresh 兼容性警告(对应 4.0.0 变更中的 #9350)。
新 JSX Transform 的检测机制
webpack 侧对 JSX runtime 的判定采用“能力探测”而非简单版本判断,见 config/webpack.config.js:
const hasJsxRuntime = (() => {
try {
if (semver.lt(require('react/package.json').version, '16.14.0')) {
return false;
}
require.resolve('react/jsx-runtime');
return true;
} catch (e) {
return false;
}
})();
当 hasJsxRuntime 为真时,Babel 的 @babel/preset-react 使用 runtime: 'automatic'(webpack 配置第 428 行),同时 ESLint 侧会关闭 react/react-in-jsx-scope 规则(第 785-788 行)。Jest 的 Babel 转换同样做了相同检测,见 config/jest/babelTransform.js。TypeScript 项目在 verifyTypeScriptSetup.js 中则要求 hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta') 时才写入新的 jsx 编译选项(scripts/utils/verifyTypeScriptSetup.js),这与 CHANGELOG 中 "Use new JSX setting with TypeScript 4.1.0"(#9734)一一对应。新模板 cra-template 也相应移除了未使用的 React import(#9853),见 cra-template/template/src/App.js。
破坏性变更(Breaking Changes)
CHANGELOG 将 4.0.0 的破坏性变更归纳为六类,逐条说明如下:
1. ESLint 升级到 7 并移除 EXTEND_ESLINT 标志
升级到 ESLint 7,新增了大量规则,包括 Jest 与 React Testing Library 规则及 import/no-anonymous-default-export 规则;eslint-plugin-hooks 升级到 4.0.0。从源码结构看,4.x 的 webpack 配置使用的是 eslint-webpack-plugin(替代了已废弃的 eslint-loader,见 config/webpack.config.js),并且不再需要用户设置 EXTEND_ESLINT=true 就能自定义 ESLint 配置——该环境变量在 4.x 源码中已无处理逻辑,与 CHANGELOG 的 "removed the EXTEND_ESLINT flag"(#9587)一致。当前 webpack 中的 ESLint 插件配置要点(webpack.config.js):
extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx']——注意mjs/cjs测试支持相关的扩展名处理;failOnError: !(isEnvDevelopment && emitErrorsAsWarnings)——开发环境可把错误降级为警告;- 缓存文件位置:
node_modules/.cache/.eslintcache。这个 "把 ESLint 缓存文件移入 node_modules" 的变更(#9977,4.0.2)在源码中直接体现为cacheLocation: path.resolve(paths.appNodeModules, '.cache/.eslintcache'),好处是缓存随node_modules一起被忽略和清理,不会污染项目目录。
2. Jest 升级到 26,默认 resetMocks: true
在 scripts/utils/createJestConfig.js 中,默认配置直接写死了 resetMocks: true。这意味着每次测试运行前 mock 会被重置,依赖 mock 调用累积的用例需要显式处理。同时该文件列出了 package.json 中允许覆盖的 Jest 键(supportedKeys,第 74-93 行),包括 resetMocks、testMatch、transform 等——4.0.2 的文档 PR #9473 "add missing override options for Jest config" 补的正是这份清单的文档化。如果覆盖了不支持的键(比如 setupFilesAfterEnv),Jest 启动时会打印明确指引:把初始化代码放进 src/setupTests.js。
3. Service Worker 切换到 Workbox InjectManifest
webpack 生产配置中的 PWA 生成逻辑见 config/webpack.config.js:
isEnvProduction &&
fs.existsSync(swSrc) &&
new WorkboxWebpackPlugin.InjectManifest({
swSrc,
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
// Bump up the default maximum size (2mb) that's precached,
// to make lazy-loading failure scenarios less likely.
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
}),
即:只有项目存在 src/service-worker.js 时才注入 service worker,采用 InjectManifest 策略(你自己维护 SW 源码,Workbox 负责预缓存清单注入);默认 2MB 的预缓存大小上限被提高到 5MB——这正是 4.0.1 中 "Increase Workbox's maximumFileSizeToCacheInBytes"(#10048,by jeffposnick)的落地结果,目的是降低懒加载资源偶发加载失败的概率。PWA 模板也随之迁移到独立的 cra-template/pwa 仓库,可独立发布。
4. 移除 --typescript 标志与 NODE_PATH 环境变量支持
创建新应用时不再支持 --typescript 标志,改用 --template typescript。在 packages/create-react-app/createReactApp.js 中,命令行帮助只保留了 --template <path-to-template> 选项,其取值可以是 npm 包名(如 cra-template-typescript)、file: 本地路径或 .tgz/.tar.gz 包地址;createApp 内通过 getTemplateInstallPackage 决定安装哪个模板包。同样,设置基础路径的旧 NODE_PATH 环境变量方式被移除,替代方案是在 jsconfig.json(或 tsconfig.json)中配置 baseUrl——仓库中的 test/fixtures/jsconfig 与 test/fixtures/typescript 测试夹具即演示了这种绝对路径导入方式。
5. 修正 dotenv 文件加载顺序
4.0.0 将 .env 系列文件的加载顺序调整为符合 dotenv 规范(PR #9037)。当前源码 config/env.js 的实现顺序为:
const dotenvFiles = [
`${paths.dotenv}.${NODE_ENV}.local`,
// Don't include `.env.local` for `test` environment
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
`${paths.dotenv}.${NODE_ENV}`,
paths.dotenv,
].filter(Boolean);
即 .env.[NODE_ENV].local > .env.local(test 环境除外)> .env.[NODE_ENV] > .env。由于 dotenv 不会覆盖已存在的环境变量,排在前面的文件优先级更高。同时该文件还处理了 NODE_PATH 的相对路径解析(仅接受相对路径,防止误导入 Node 核心模块),并通过 REACT_APP_ 前缀过滤出注入客户端的变量(第 69 行)。
6. 停止支持 Node 8
Node 8 已于 2019 年底到达生命周期终点,4.x 不再支持。从源码结构看,4.x 中各包(如 babel-preset-react-app、react-scripts)的依赖声明已按更高 Node 基线调整(#8948),文档中也移除了 Node 8 的引用(#10214,4.0.2 文档变更)。
4.0.0 其他值得注意的变更
- 新增功能(节选自 CHANGELOG 明细):
- AVIF 图片支持(#9611);
- 允许在
package.json中覆盖 Jest 的testMatch(#9114); - 恢复
--stats输出 webpack 统计(#8790); - 模板支持
devDependencies(#8838)——模板的package.json中的devDependencies会被合并进新创建项目的devDependencies,这正是 createReactApp.js 中模板依赖处理逻辑支撑的能力; - 检测到 create-react-app 版本过旧时退出提示(#9359)。
- Bug 修复(节选):新 JSX Transform 相关问题修复(#9788)、ESLint 配置改为从
appPath解析(#9683)、测试运行器支持.cjs/.mjs(#8768,与createJestConfig.js中transform正则包含cjs|mjs一致)、refreshOverlayInterop模块作用域错误修复(#9805)。 - 增强(节选):开发环境 scss source map(#8638)、React 低于 16.10 时的 Fast Refresh 警告(#9350)、模板测试更新与 web-vitals 性能上报(#9116,模板中的 reportWebVitals.js 即该能力的落地)、Yarn 2
--use-pnp修复(#8460)。 - 底层工具(节选):Jest 26 升级(#8955)、
eslint-loader替换为eslint-webpack-plugin(#9751)、依赖大版本升级(#8950)。
4.0.0 是 4.x 系列改动最大的版本,CHANGELOG 记录了 63 位贡献者。对于 3.4.x 用户,除了执行上述迁移命令外,务必对照本文"破坏性变更"一节检查 ESLint 自定义规则、Jest mock 用法、service worker 源码(需自行提供 src/service-worker.js 以启用 InjectManifest 流程)以及 Node 版本。
4.0.1:聚焦稳定性与维护体验的补丁版
4.0.1(2020-11-23)是维护版,包含小 bug 修复与文档更新,主要变更:
- Bug 修复:
noFallthroughCasesInSwitch/jsx object is not extensible 问题修复(#9921);react-jsx报错修复(#9869);- eject 之后
React is not defined编译错误修复(#9885); - 重新编译缓慢问题修复(#9911);
FAST_REFRESH=false时页面不刷新的问题修复(#9884)——如前所述,该开关经由 config/env.js 与 webpackHotDevClient.js 传递,是排查"关闭 Fast Refresh 后 HMR 失效"类问题的关键链路。
- 增强:调高 Workbox 的
maximumFileSizeToCacheInBytes(#10048),源码中现为 5MB(见上文 webpack 配置摘录)。 - 文档:在"需要 jsdom 的库"列表中补充 React Testing Library(#10052)。
- 内部:用
prompts替换inquirer(#10083,影响create-react-app与react-scripts的交互输入实现)、模板图片优化(#9516)。
迁移方式:
npm install --save --save-exact react-scripts@4.0.1
# 或
yarn add --exact react-scripts@4.0.1
4.0.2:BUILD_PATH 高级配置变量登场
4.0.2(2021-02-03)同样为维护版,但它带来了一个实用的新配置项:
新特性:BUILD_PATH 环境变量
PR #8986 增加了 BUILD_PATH 高级配置变量,允许覆盖默认的 build 输出目录。在 config/paths.js 中实现极为简洁:
const buildPath = process.env.BUILD_PATH || 'build';
即构建产物默认输出到 build/,设置 BUILD_PATH=dist 等即可改为其他目录,无需 eject。这与 advanced-configuration 文档中"不 eject 就能调整构建路径"的场景直接对应(参见 docusaurus/docs/advanced-configuration.md)。
其他变更(4.0.2)
- Bug 修复:
- 为
eslint-webpack-plugin增加退出开关(#10170)——对应源码中的DISABLE_ESLINT_PLUGIN环境变量:config/webpack.config.js 有const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === 'true';,仅当显式设置为字符串'true'时整个 ESLint 插件从plugins数组中剔除(第 766 行)。设置方式如DISABLE_ESLINT_PLUGIN=true npm run build; - 补齐
react缺失的 peer 依赖并更新react-refresh-webpack-plugin(#9872); react-scripts增加 TypeScript 4.x 作为 peerDependency(#9964)。
- 为
- 增强:ESLint 缓存文件移入
node_modules(#9977,见前文cacheLocation源码)、改进开发环境 vendor chunk 命名(#9569)。 - 文档:补充 Jest 配置可覆盖选项说明(#9473)、更新 public 目录文档(#10314)、移除 Node 8 相关表述(#10214)。
- 内部:用 immer 处理
appTsConfig不可变性(#10027,实现见 packages/react-dev-utils/immer.js)、恢复部分集成测试(#10091)。 - 底层工具:升级
sass-loader(#9988)、更新 postcss 系列后又回滚(#10003、#10216)、升级@svgr/webpack修复构建错误(#10213)、formatWebpackMessages移除 chalk 依赖(#10198)、TypeScript 模板版本提升(#10141)。
迁移方式:
npm install --save --save-exact react-scripts@4.0.2
# 或
yarn add --exact react-scripts@4.0.2
4.0.3:4.x 系列的收尾维护版
4.0.3(2021-02-22)是 4.x 系列 CHANGELOG 记录的最后一个版本,包含少量修复:
- Bug 修复:升级
eslint-webpack-plugin以修复 opt-out 标志(DISABLE_ESLINT_PLUGIN)失效的问题(#10590)——这恰好呼应 4.0.2 引入的退出开关:开关语义为"仅当等于字符串true时生效",4.0.3 通过升级插件保证了该判断在插件侧行为正确; - 内部:
react-dev-utils升级 immer 至 8.0.1 以修复安全漏洞(#10412,immer 的封装入口为 packages/react-dev-utils/immer.js);create-react-app更新测试用例使其与描述一致(#10384,对应 tests/getTemplateInstallPackage.test.js)。
迁移方式:
npm install --save --save-exact react-scripts@4.0.3
# 或
yarn add --exact react-scripts@4.0.3
升级实践要点总结
-
统一迁移命令:4.x 系列所有版本升级都遵循"未 eject 则精确升级 react-scripts"的原则:
npm install --save --save-exact react-scripts@<版本>或yarn add --exact react-scripts@<版本>;3.x 升 4.0.0 时如遇依赖问题,删node_modules重装是官方认可的兜底手段。 -
3.4.x 升 4.0.0 的检查清单:
- ESLint:确认自定义规则与 ESLint 7 兼容;不再需要
EXTEND_ESLINT;新增 Jest/testing-library/import 规则可能产生新告警; - Jest:
resetMocks: true成为默认,mock 相关用例需复查;可在package.json的jest字段覆盖 supportedKeys 列出的选项; - PWA:需按 InjectManifest 模式提供
src/service-worker.js;预缓存上限 5MB; - 创建新项目:用
--template typescript代替--typescript;基础路径用jsconfig.json的baseUrl代替NODE_PATH环境变量; - 运行环境:Node 8 不再受支持。
- ESLint:确认自定义规则与 ESLint 7 兼容;不再需要
-
常用环境变量速查(均已在 4.x 源码中验证):
FAST_REFRESH=false:关闭 Fast Refresh(env.js);DISABLE_ESLINT_PLUGIN=true:跳过构建时 ESLint 检查(webpack.config.js);BUILD_PATH=dist:自定义构建输出目录(paths.js);.env加载优先级:.env.[NODE_ENV].local>.env.local(test 除外)>.env.[NODE_ENV]>.env(env.js)。
-
历史版本溯源:4.x 之前的版本请查阅 CHANGELOG-3.x.md(CHANGELOG 文末亦如此指引),更早的还可看 CHANGELOG-2.x.md、CHANGELOG-1.x.md 与 CHANGELOG-0.x.md。
适用前提与限制说明
- 本文所有版本号、变更条目、迁移命令均直接取自仓库根目录的 CHANGELOG-4.x.md;源码验证基于当前仓库快照(4.x 末期的
packages/代码),若你在 4.x 之外的版本上操作,行为可能不同。 - 变更条目中的 PR 编号、贡献者名单为 CHANGELOG 原文记录;本文未逐一验证每个 PR 细节,仅对文中重点特性(Fast Refresh、JSX runtime、dotenv 顺序、Jest resetMocks、Workbox InjectManifest、ESLint 插件与缓存、BUILD_PATH)给出了源码级佐证路径。
- 部分能力(如 Fast Refresh)在 4.0.0 时标注为"实验性",实际使用中建议以当时版本 README 与官方文档为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00