设置 React 项目
React 应用需要一些工具配置才能运行。需要有工具把 JSX 转换成浏览器能执行的 JavaScript,还需要有工具在你开发时提供本地服务。Vite 两件事都能做,而且可以在约三十秒内搭建一个完整的项目。本章会从空文件夹带你到一个运行中的应用,然后追踪最重要的一条路径:组件是如何最终出现在屏幕上的。
创建项目
Vite 是一个内置项目生成器的构建工具。用 npm create 运行它,回答两个提示问题,你就有了一个项目:
npm create vite@latest my-react-app
cd my-react-app
npm install
npm run dev第一条命令会问你想用哪个框架(选 React)以及想用哪个变体(选 JavaScript,或者如果你用 TypeScript 就选 TypeScript)。它会创建一个名为 my-react-app 的文件夹,里面有 React 项目需要的所有东西。npm install 会下载 package.json 里列出的依赖,React 本身就来自这里。npm run dev 启动开发服务器并输出一个本地地址,通常是 http://localhost:5173。在浏览器里打开这个地址,启动应用就运行起来了。
生成的文件夹里有几个早期重要的文件:
index.html:浏览器加载的单一 HTML 页面。src/main.jsx:入口文件,React 在这里附加到该页面。src/App.jsx:你的顶层组件,也是你最先编辑的文件。src/assets/:你在组件里引入的图片和其他静态文件。package.json:依赖和dev、build、preview脚本。vite.config.js:构建配置,你可以长时间不用理它。
各部分如何关联
三个文件按顺序相互传递。从 index.html 开始,它故意几乎是空的:
<body>
<div id="root"></div>
<!-- 后面跟一个 <script type="module" src="/src/main.jsx"> 标签 -->
</body>这个空的 <div id="root"> 是页面上 React 被允许填充的地方。你的应用渲染的一切都会放在里面。下面的 module <script> 标签加载 main.jsx,这是 React 接管的地方:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './index.css'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>
)从右往左读这个调用。document.getElementById('root') 找到那个空的 div。createRoot 把它包装在一个 React root 里,意思是 React 现在拥有那个元素的内容,并将使其与你的组件保持同步。.render(...) 告诉它要放什么进去:App 组件。
<StrictMode> 是一个仅在开发时有效的包装器,它会对里面的所有内容运行额外检查,并对可能导致 bug 的模式发出警告。它不会给页面添加任何东西,在生产构建中会自动关闭。它最显著的作用出现在 Effects 里,它故意在开发期间挂载每个组件两次。
App.jsx 是一个普通的组件,它导出自己:
export default function App() {
return <h1>来自 React 的问候</h1>
}这就是整个链条。浏览器加载 index.html,脚本加载 main.jsx,main.jsx 在 root div 上调用 createRoot 并渲染 <App />,App 返回的 JSX 变成你看到的标题。从这里开始你写的每个组件都坐在 App 里的某个地方,所以它通过这同一条路径到达页面。main.jsx 通常写一次就很少再改动;你几乎所有的工作都发生在 App.jsx 和它引入的组件里。
版本说明
React 17 及更早版本使用 ReactDOM.render(<App />, document.getElementById('root')) 作为入口点。React 18 引入了 createRoot,它启用了并发渲染,旧的调用方式仍然可用但有弃用警告。React 19 移除了它,所以 ReactDOM.render 现在会抛出错误。在仍然使用它的教程上,createRoot 是要写的替代行。参见 历史和版本 了解什么改变了。
开发服务器
npm run dev 启动 Vite 的开发服务器,它会在你工作时保持在终端运行。它的工作是提供你的应用服务,并对你的编辑做出反应。
保存对 App.jsx 的更改,浏览器会几乎立即更新,不需要手动刷新。这叫热模块替换:Vite 把改变的模块推送到页面并进行替换,让应用的其余部分保持运行。一个你点过的计数器如果显示七,编辑后通常还会显示七,所以你在调整周围的标记时保持了你设置的任何状态。某些改动仍然会强制完全重新加载,Vite 会为你判断。
项目自带两个其他脚本。npm run build 在 dist 文件夹里产生一个优化的包,这是你要部署的东西。npm run preview 在本地提供那个构建后的输出,这样你可以在发布前检查它。在开发期间,npm run dev 是你唯一需要的。
导入静态资源
图片存放在 src/assets 下面,通过 import 引入到组件里:
import logo from './assets/logo.png'
export default function Header() {
return <img src={logo} alt="公司标志" />
}import 给你一个持有那个图片最终 URL 的变量,然后你把它传给 src,用花括号包起来。把 src="./assets/logo.png" 写成一个纯字符串往往会破坏,因为你源文件夹里的路径很少会是构建后输出的路径。
改用 import 把构建工具纳入流程。它看到你的代码依赖 logo.png,把它复制到输出里,给你回一个那个文件最终出现的任何 URL。把你的组件移到另一个文件夹,相对 import 仍然会解析。拼错文件名,你会立即发现,因为 import 会失败,而不是在生产环境中发现一个破损的图片。同样的模式对 SVG、字体和任何其他构建工具知道如何处理的文件都有效。
值得记住的是这条链:index.html 有一个 id 为 root 的空 div,main.jsx 用 createRoot 抓住那个 div 并把 <App /> 渲染进去,App.jsx 是说明什么会出现的组件。
一旦你能跟踪那条路径,你就确切知道你自己的代码插在哪里。
接下来:组件,是你会用来填充 App.jsx 的构建块。

