4.1 脚手架工具 本节摘要:脚手架决定项目的起点。Create React App 曾经是默认答案,如今维护放缓、构建慢,已不再是新项目首选;Vite 靠原生 ES 模块的按需编译成为主流。本节对比两者全貌——目录结构、配置方式、性能与生态——并给出新项目与存量项目的选型路径。 本节目标 阅读完本节,你应当能够: 说出 CRA 与 Vite 各自的工作原理与性能差异 解释 CRA 为什么不再适合新项目 用 Vite 创建 React 项目并理解关键配置 判断存量 CRA 项目该不该迁移 一、问题与直觉:脚手架不是「选一个跑起来」那么简单 脚手架的选择在项目早期看似随意——「能跑就行」。但三个月后你会发现问题:要加一个功能,CRA 项目要先 eject 或装一堆 hack 插件;
本节摘要:脚手架决定项目的起点。Create React App 曾经是默认答案,如今维护放缓、构建慢,已不再是新项目首选;Vite 靠原生 ES 模块的按需编译成为主流。本节对比两者全貌——目录结构、配置方式、性能与生态——并给出新项目与存量项目的选型路径。
阅读完本节,你应当能够:
脚手架的选择在项目早期看似随意——「能跑就行」。但三个月后你会发现问题:要加一个功能,CRA 项目要先 eject 或装一堆 hack 插件;Vite 项目改一行配置就行。脚手架决定的是「后面每次遇到问题时的解决成本」。
另一个容易忽视的层面是团队心智:团队越熟悉某个脚手架的目录结构和配置方式,换工具的学习成本越高。所以脚手架不只是「技术选型」,还是「团队投入」——选之前想清楚「这是打算用一两年的工具」,别当一次性决定。这也是为什么「稳定压倒一切」——脚手架换得频繁,团队光适应环境就消耗大量精力。
第 1.2 节已经介绍过两者的基本用法,本节从生态视角看现状:CRA 由 Facebook 官方维护,一度是 React 的默认脚手架;Vite 由 Vue 作者尤雨溪开发,如今已是构建工具的主流。时间站在 Vite 这边。
CRA 的价值是「零配置起步」——预配置 Babel、Webpack、代码检查、测试环境,一条命令建出可运行的项目。它让 React 入门门槛大幅降低,也定义了「标准项目结构」。这套结构的价值至今仍在:很多现代脚手架的目录布局都能看到 CRA 的影子,它是 React 项目结构的「共同语言」。
CRA 的问题在生态发展中暴露:
构建慢。底层是 Webpack 全量打包,项目一大,冷启动和热更新都变慢。现代开发对「秒级热更新」的期待,CRA 满足不了。
维护放缓。React 官方团队已把精力转向框架层(Next.js),CRA 的更新节奏明显放慢,对新工具(如 React 19)的适配滞后。
定制困难。需要自定义配置时只能 eject——把内部配置全部吐出来,从此自己维护,不可逆。而 Vite 的配置是「改文件」级别的。
| 维度 | CRA | Vite |
|---|---|---|
| 底层 | Webpack | 开发期原生 ESM,构建期 Rollup |
| 冷启动 | 全量打包,慢 | 按需编译,快 |
| 热更新 | 一般 | 快 |
| 配置 | 隐藏,eject 才能改 | 开放,vite.config 直接改 |
| 维护 | 放缓 | 活跃 |
| 生态 | 成熟 | 迅速补齐 |
Vite 的开发服务器不做「全量打包」,而是利用浏览器原生 ES 模块——浏览器请求哪个模块,Vite 就编译哪个。项目再大,冷启动也只编译被请求的部分。

这张图把两种构建的差异画在对比里:左侧 Webpack 是「一次打包所有」,右侧 Vite 是「用到多少编译多少」。和上面的 mermaid 流程图互补——那张看 Vite 的单次流程,这张看两种方式的整体差异。
npm create vite my-app -- --template react cd my-app npm install npm run dev
生成的结构比 CRA 精简:index.html 在根目录,入口是 main.jsx,配置在 vite.config.js。
两个脚手架的骨架放到一起看:
# CRA 结构 my-app/ public/ # 静态资源,index.html 在这里 src/ App.js # 根组件 index.js # 入口 App.css # 根组件样式 index.css # 全局样式 # Vite 结构 my-vite-app/ index.html # 入口在根目录(不是 public) vite.config.js # 配置文件 src/ App.jsx # 根组件 main.jsx # 入口 App.css index.css
最明显的差异:CRA 的 HTML 模板藏在 public 里,Vite 的 index.html 直接裸露在根目录——它是整个应用的真正入口,引用了 main.jsx。这种「入口透明」的风格贯穿 Vite 的设计:没有隐藏层,所见即所配。
项目里不同环境(开发、测试、生产)的配置(API 地址、开关)用环境变量管理:
// 在 .env 文件里定义 // VITE_API_URL=http://localhost:3000 // 组件里读取 const apiUrl = import.meta.env.VITE_API_URL;
Vite 用 import.meta.env.VITE_ 前缀读取环境变量,比 CRA 的 process.env.REACT_APP_ 前缀短且统一。注意 VITE_ 前缀是「暴露给前端」的约定——不带前缀的变量不会打进前端代码,避免密钥泄露。这一点在前后端分离的项目里尤其重要:前端代码会被任何人看到,密钥只能放服务端。
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], server: { port: 5173, }, build: { outDir: 'dist', }, });
插件系统是 Vite 扩展能力的入口——react 插件负责 JSX 转译,其他能力(代码检查、路径别名、代理)都以插件或配置项接入。遇到「CRA 里要装包 hack 的事」,Vite 里往往是改个配置或装个插件的事。
开发时前端要请求后端接口,跨域问题用代理解决:
export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, }, }, }, });
浏览器里请求 /api/xxx 会被 Vite 代理到后端地址,绕开跨域。这个配置是前后端联调的第一步,比 CRA 时代需要额外配置 proxy 中间件方便得多。注意代理只在开发服务器生效,生产环境的跨域要靠网关或后端配置解决——代理不是部署方案。
一个现实问题:团队手上已有 CRA 项目,要不要迁到 Vite?
不迁的理由:项目稳定运行、没有明显构建慢的痛点、迁移成本(配置对不齐、第三方插件兼容)大于收益。
迁的理由:构建慢到影响开发体验、需要 CRA 给不了的新能力、项目还在快速迭代期(迁移成本分摊在后续开发里)。
一个折中的判断:项目稳定就留着,项目活跃就值得迁。 迁移本身是「换构建层,不动业务代码」——React 代码和构建工具是解耦的,Vite 官方也提供迁移指南。但任何迁移都有回归风险,要配好测试再动手。
如果决定迁移,按这个顺序推进风险最低:
第一,用 Vite 建一个空项目,把入口文件和依赖对齐。第二,逐个迁移 src 下的业务代码——组件、状态、工具函数基本原样可用,要改的主要是「import 方式」和「环境变量前缀」。第三,改配置文件:路径别名、代理、构建输出对齐旧项目。第四,跑测试和构建,验证产物一致。第五,灰度上线。
有一个容易被忽略的点:CRA 时代有些「隐形魔法」在 Vite 里不存在。比如 CRA 自动引入全局样式、自动处理 SVG 引用,Vite 需要显式配置。迁移时逐项对照「旧项目里哪些东西是 CRA 白送的」,否则上线后才发现样式或资源静默丢失。
生产构建:CRA 用 Webpack 打包成 bundle,Vite 用 Rollup 打包。两者产物都能上线,但体积和分割策略有差异。Vite 默认按「动态 import」自动分包,CRA 需要手动配。配合第 3.1 节的代码分割,Vite 的项目通常首屏更小——这是 Vite 在生产侧的一个隐性优势,也是大项目对比构建工具时常被忽略的维度。
CRA 内置了 Jest 测试环境,Vite 默认不带。需要配 Vitest(Vite 生态的测试框架,API 兼容 Jest)或手动接 Jest。对已有测试的项目,这个差异在迁移清单里要占一格。Vitest 复用 Vite 的配置,不用单独配转译,迁移成本反而低。
⚠️ 常见坑:以为脚手架选型是一次性的。工具链会老化,两年回头看一次「当前项目的工具还是最优解吗」是值得的习惯。但别为了追新而频繁迁移——工具是服务的,稳定压倒一切。
💡 关键直觉:选脚手架看三个指标——冷启动速度、配置自由度、维护活跃度。2026 年的今天三个指标都指向 Vite;CRA 的价值是历史教科书和存量项目。新项目直接 Vite,别犹豫。
Vite 建出来是「最小可跑项目」,真实项目还要加代码检查、格式化、测试、路径别名。现在团队常用「基于 Vite 的内部模板」或社区模板起步——一次配好,团队复用。这也引出一个决策:从零配 vs 用模板。用模板快但有黑盒,从零配慢但透明。我的建议是至少从头配一次理解每个配置的用途,之后再用模板提速。
一个「够用」的 Vite React 模板,除了基本依赖,通常会预置五样东西:
代码规范:ESLint + Prettier 配置,配好 React 插件与格式化规则。路径别名:@ 指向 src,组件 import 不再写一长串相对路径。测试环境:Vitest + Testing Library 配置,新组件顺手写测试。环境变量模板:.env.example 列出所有变量名,新人不用猜。CI 脚本:lint、test、build 串成一条命令,提交前跑一遍。
这五样预置的价值是「把团队约定固化进模板」——新成员 clone 下来就能按团队规范开发,不用逐个问「我们项目怎么跑测试」。
用模板没问题,但「至少从零配一次」的收益值得说清楚:从零配让你知道每个配置项为什么存在。比如你亲手配过 server.proxy,就知道「开发时跨域为什么能通」;亲手配过路径别名,就理解「@ 指向哪、为什么别乱改」。这些理解在遇到「模板跑不起来」的诡异问题时是救命线索——你知道该查配置的哪一处,而不是对着报错干瞪眼。
最后补一句关于「工具迷信」的话:脚手架和模板再方便,也只是起点。真正决定项目质量的,是项目里的代码组织与团队规范——第 5 章会讲。别花太多时间「比较脚手架」,够用就行;把精力留给业务代码和架构设计,它们的回报率高得多。
「Vite 的热更新为什么快?」 核心在「按需编译 + 依赖预构建」。开发期 Vite 不做全量打包,浏览器请求哪个模块才编译哪个——项目再大,改一个文件只重新编译这一个文件。依赖(node_modules 里的库)在启动时用 esbuild 预构建成 ESM 缓存,之后不再重新编译。对比 Webpack 的全量打包,Vite 把「改一行等几秒」变成「改一行等几百毫秒」。理解了「按需」两个字,很多 Vite 的配置行为(比如为什么有些文件没被处理)都能想通——不是它漏了,是它只处理被请求的部分。
「Vite 生产构建和开发是同一套吗?」 不是。开发期是「原生 ESM + 按需编译」,生产构建用的是 Rollup——因为原生 ESM 有性能开销(模块数量多时网络请求多),生产要打包合并。所以「开发期很快」和「生产产物质量」是两件事,各自有各自的机制。这带来一个实践含义:开发时看到的依赖解析、代码转译方式,和生产构建可能不同——遇到「开发正常、构建报错」,多半是「只依赖开发期特性」导致的。检查生产构建要在本地先跑一遍 npm run build 验证,别等上线才发现。
「Vite 项目怎么加路径别名?」 配置 resolve.alias。开发和生产都要配——alias 在开发期由 Vite 自己解析,生产期由 Rollup 走同一份配置。配置后组件里就能写 import Button from '@/components/Button',告别一长串相对路径。注意别只配 Vite 而不配编辑器/代码检查工具:路径别名需要 IDE 的 tsconfig(TS 项目)或 jsconfig 同步理解,否则编辑器里跳转失效、代码检查报「找不到模块」。配一处、全家同步,是路径别名的正确姿势——这也是 4.1 节「模板通常预置路径别名」里说的那件事。
「CRA 项目里的测试能直接搬到 Vite 吗?」 测试框架层面可以,环境层面要适配。CRA 内置 Jest 配置(babel-jest 转译、jsdom 环境、测试路径约定),Vite 不内置测试环境。两个迁移路径:一是装 Vitest——它复用 Vite 的转译和配置,API 兼容 Jest 的 describe/it/expect,迁移成本最低;二是继续用 Jest 但手动配 transform(把 Vite 的转译插进去)。绝大多数项目选 Vitest 更顺——和 Vite 共享配置、启动快、TypeScript 支持好。有大量存量 Jest 测试的话,评估一下工作量再决定,别为了「统一」硬迁。
下一节看状态管理库生态——脚手架建好项目,状态管理决定数据怎么流动。Redux 全家桶、Zustand、Recoil 的周边配套与适用边界,第 3.3 节的选型框架在这里细化成库生态的完整视图,让你知道除了核心库本身,还有哪些中间件和工具值得用。