React: 环境搭建与 JSX
最后更新:2026-08-26
写 React 代码就像装修房子——Vite 是施工队(帮你搭建脚手架和开发服务器),JSX 是装修图纸(一种在 JavaScript 里写 HTML 的语法)。这一课你会亲手搭建第一个 React 项目。
1. 你将学到
- 使用 Vite 快速创建 React 项目
- 项目目录结构和各文件的作用
- JSX 语法规则(表达式、条件、列表)
- JSX 与 HTML 的核心差异
- 安装和使用 React DevTools
2. 一个新手开发者的真实故事
(1) 痛点:搭建环境花了一整天
Bob 刚学会 JavaScript,想试试 React。他在网上搜教程,有人推荐 Create React App,有人推荐 Vite,还有人推荐 Next.js。他随便选了 CRA 开始安装:
BASH
npx create-react-app my-app
结果:
- 安装花了 8 分钟(下载 200MB+ 的依赖包)
- 启动开发服务器花了 30 秒
- 项目目录里塞满了 35,000+ 个文件(node_modules)
- 改一行代码,页面刷新要等 2-3 秒
Bob 很沮丧:"我还没开始写代码,就已经浪费了一个小时。"
(2) Vite + React 的解法
用 Vite 创建同样的项目:
BASH
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev
| 对比 | CRA | Vite |
|---|---|---|
| 安装时间 | ~8 分钟 | ~30 秒 |
| 启动速度 | ~30 秒 | <1 秒 |
| 热更新 (HMR) | 2-3 秒 | <50ms |
| 构建时间 | ~60 秒 | ~10 秒 |
| node_modules 大小 | ~200MB | ~80MB |
收益:Bob 从安装到看到 Hello World,只用了 3 分钟,而且热更新快到几乎感觉不到等待。
3. JSX 的核心规则
JSX 是 JavaScript XML 的缩写,它不是 HTML,而是一种 JavaScript 语法扩展。每个 JSX 表达式最终会被编译成 React.createElement() 调用。
graph LR
A[JSX 代码] --> B[Babel 编译]
B --> C[React.createElement]
C --> D[虚拟 DOM 对象]
D --> E[真实 DOM]
style A fill:#61dafb,color:#000
style C fill:#ff6b6b,color:#fff
(1) 单根元素规则
JSX 表达式必须有一个根元素。不能返回两个并列的兄弟元素。
JSX
// ❌ 错误:没有根元素
return (
<h1>标题</h1>
<p>段落</p>
)
// ✅ 正确:用一个 <div> 包裹
return (
<div>
<h1>标题</h1>
<p>段落</p>
</div>
)
// ✅ 也可以使用 React Fragment(<></>),不产生额外 DOM
return (
<>
<h1>标题</h1>
<p>段落</p>
</>
)
(2) JSX 中嵌入 JavaScript 表达式
用 花括号 {} 嵌入任何 JavaScript 表达式:
JSX
const name = 'Alice'
const age = 28
const colors = ['red', 'green', 'blue']
// 变量
<h1>Hello, {name}!</h1>
// 表达式计算
<p>明年 {age + 1} 岁</p>
// 三元运算符
<span>{age >= 18 ? '成年' : '未成年'}</span>
// 函数调用
<p>今天日期:{new Date().toLocaleDateString()}</p>
// 数组长度
<p>共有 {colors.length} 种颜色</p>
(3) JSX 与 HTML 的差异速查表
| 特性 | HTML | JSX |
|---|---|---|
| 类名 | class="box" |
className="box"(class 是 JS 保留字) |
| 标签属性 | for="input" |
htmlFor="input"(for 是 JS 保留字) |
| 内联样式 | style="color:red" |
style={{ color: 'red' }}(对象语法) |
| 布尔属性 | disabled |
disabled={true} 或 disabled |
| 自闭合标签 | <br> <img> |
必须闭合:<br /> <img /> |
| 注释 | <!-- 注释 --> |
{/* 注释 */} |
| 事件绑定 | onclick="handle()" |
onClick={handle} |
| CSS 属性名 | font-size(连字符) |
fontSize(驼峰命名) |
▶ 示例:JSX vs HTML 对比
JSX
// ============================================
// 示例:同一个卡片,HTML vs JSX 写法对比
// ============================================
// ---- HTML 写法 ----
<div class="card" style="background-color: #f0f0f0; padding: 20px;">
<!-- TODO: 替换为实际用户头像图片 -->
<img src="https://i.pravatar.cc/80" alt="头像" class="avatar">
<label for="name">姓名:</label>
<input type="text" id="name" disabled>
<!-- 这是一个注释 -->
<button onclick="handleClick()">点击</button>
</div>
// ---- JSX 写法 ----
<div className="card" style={{ backgroundColor: '#f0f0f0', padding: '20px' }}>
<!-- TODO: 替换为实际用户头像图片 -->
<img src="https://i.pravatar.cc/80" alt="头像" className="avatar" />
<label htmlFor="name">姓名:</label>
<input type="text" id="name" disabled={true} />
{/* 这是一个注释 */}
<button onClick={handleClick}>点击</button>
</div>
4. 使用 Vite 创建 React 项目
(1) 环境要求
| 工具 | 版本要求 | 检查命令 | 安装方式 |
|---|---|---|---|
| Node.js | ≥ 18.0 | node -v |
nodejs.org |
| npm | ≥ 9.0 | npm -v |
随 Node.js 自动安装 |
| 编辑器 | 任意 | — | 推荐 VS Code |
(2) 创建项目并启动
BASH
# 1. 创建项目(react-ts = React + TypeScript 模板)
npm create vite@latest my-react-app -- --template react-ts
# 2. 进入项目目录
cd my-react-app
# 3. 安装依赖
npm install
# 4. 启动开发服务器
npm run dev
输出:
TEXT
📖 仅展示
VITE v5.x ready in 320ms
➜ Local: http://localhost:5173/
➜ Network: http://192.168.1.100:5173/
打开 http://localhost:5173/,你应该能看到 Vite + React 的默认欢迎页面。
(3) 项目目录结构
BASH
my-react-app/
├── index.html # 入口 HTML 文件(Vite 解析入口)
├── package.json # 项目配置和依赖
├── vite.config.ts # Vite 配置文件
├── tsconfig.json # TypeScript 配置
├── tsconfig.app.json # 应用 TS 配置
├── tsconfig.node.json # Node.js 环境 TS 配置
├── public/ # 静态资源(不会被编译)
│ └── vite.svg
└── src/ # 源代码目录
├── main.tsx # 应用入口(渲染根组件)
├── App.tsx # 根组件
├── App.css # 根组件样式
├── index.css # 全局样式
└── assets/ # 资源文件(会被编译)
└── react.svg
| 文件 | 作用 |
|---|---|
index.html |
页面入口,Vite 从这里开始解析 |
src/main.tsx |
JavaScript 入口,渲染 <App /> 到 #root |
src/App.tsx |
根组件,所有其他组件从这里开始 |
vite.config.ts |
Vite 配置(别名、代理、插件等) |
▶ 示例:创建一个最简单的 React 组件
TSX
// ============================================
// 示例:从 main.tsx 到页面的完整流程
// ============================================
// src/main.tsx —— 应用入口
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import './index.css'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>
)
TSX
// src/App.tsx —— 根组件
function App() {
return (
<div>
<h1>Hello, React!</h1>
<p>这是我的第一个 React 应用 🎉</p>
</div>
)
}
export default App
CSS
/* src/index.css —— 全局样式 */
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
text-align: center;
}
5. React DevTools
React DevTools 是调试 React 应用的必备工具,可以查看组件树、Props、State 和性能。
| 安装方式 | 操作 |
|---|---|
| Chrome 扩展 | Chrome Web Store 搜索 "React Developer Tools" |
| Firefox 扩展 | Firefox Add-ons 搜索 "React Developer Tools" |
| 独立应用 | npx react-devtools |
▶ 示例:使用 DevTools 查看组件
安装后打开开发者工具(F12),会出现 Components 和 Profiler 两个新标签页:
JSX
// 在 DevTools 中可以实时查看这些组件的 Props 和 State
function App() {
const [count, setCount] = React.useState(0)
return (
<div>
<Header title="计数器" />
<Counter count={count} onIncrement={() => setCount(c => c + 1)} />
<Footer year={2026} />
</div>
)
}
// Components 面板中会显示:
// ├── App
// │ ├── Header (props: { title: "计数器" })
// │ ├── Counter (props: { count: 0, onIncrement: fn })
// │ └── Footer (props: { year: 2026 })
▶ 示例 4:JSX 条件渲染与列表综合
JSX
function ProductCatalog({ products, isLoading, error }) {
if (isLoading) return <p style={{ textAlign: 'center', padding: 40 }}>Loading products...</p>
if (error) return <p style={{ color: '#ff4d4f', textAlign: 'center' }}>Error: {error}</p>
if (products.length === 0) return <p style={{ textAlign: 'center', color: '#999' }}>No products found</p>
return (
<div style={{ maxWidth: 600, margin: '0 auto' }}>
<h2>Product Catalog ({products.length})</h2>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(180px, 1fr))', gap: 16 }}>
{products.map(product => (
<div key={product.id} style={{ border: '1px solid #eee', borderRadius: 8, padding: 12 }}>
<h4 style={{ margin: '0 0 8px' }}>{product.name}</h4>
<p style={{ color: '#ff4d4f', fontWeight: 'bold', margin: '0 0 4px' }}>${product.price}</p>
{product.inStock ? (
<span style={{ color: '#52c41a', fontSize: 12 }}>In Stock</span>
) : (
<span style={{ color: '#999', fontSize: 12 }}>Out of Stock</span>
)}
</div>
))}
</div>
</div>
)
}
function CatalogPage() {
const sampleProducts = [
{ id: 1, name: 'Keyboard', price: 79, inStock: true },
{ id: 2, name: 'Mouse', price: 49, inStock: true },
{ id: 3, name: 'Monitor', price: 399, inStock: false },
]
return <ProductCatalog products={sampleProducts} isLoading={false} error={null} />
}
▶ 示例 5:综合——React 项目入口文件解析
JSX
📖 仅展示
// package.json 关键依赖
// {
// "dependencies": { "react": "^18.3", "react-dom": "^18.3" },
// "devDependencies": { "vite": "^5.0", "@types/react": "^18.3", "@types/react-dom": "^18.3" }
// }
// src/main.jsx - 应用入口
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
)
// src/App.jsx - 根组件
import { useState } from 'react'
function App() {
const [currentPage, setCurrentPage] = useState('home')
return (
<div style={{ fontFamily: 'sans-serif', maxWidth: 800, margin: '0 auto', padding: 20 }}>
<nav style={{ display: 'flex', gap: 16, marginBottom: 24, borderBottom: '2px solid #1890ff', paddingBottom: 12 }}>
{['home', 'about', 'contact'].map(page => (
<button key={page} onClick={() => setCurrentPage(page)}
style={{
padding: '8px 16px', border: 'none', borderRadius: 4, cursor: 'pointer',
background: currentPage === page ? '#1890ff' : '#f0f0f0',
color: currentPage === page ? 'white' : '#333',
}}>
{page.charAt(0).toUpperCase() + page.slice(1)}
</button>
))}
</nav>
<main>
{currentPage === 'home' && (
<div>
<h1>Welcome to React</h1>
<p>This is a minimal React app with Vite. Edit src/App.jsx and save to reload.</p>
<p>Current time: {new Date().toLocaleTimeString()}</p>
</div>
)}
{currentPage === 'about' && (
<div>
<h2>About</h2>
<p>Built with React 18 + Vite for fast development and HMR.</p>
</div>
)}
{currentPage === 'contact' && (
<div>
<h2>Contact</h2>
<p>Email us at hello@example.com</p>
</div>
)}
</main>
<footer style={{ marginTop: 40, paddingTop: 12, borderTop: '1px solid #eee', color: '#999', fontSize: 13 }}>
© 2026 React Tutorial. Built with Vite + React 18.
</footer>
</div>
)
}
export default App
❓ 常见问题
Q npm create vite@latest 和 npx create-vite 有什么区别?
A 功能完全一样。
npm create vite@latest 是 npm 的推荐写法(npm v7+ 引入),会自动找到最新的 create-vite 包并执行。npx create-vite 也可以使用。如果你用的是 pnpm 或 yarn,可以用 pnpm create vite 或 yarn create vite。Q JSX 里为什么不能用 class,要用 className?
A 因为
class 是 JavaScript 的保留关键字。JSX 本质是 JavaScript,所以不能使用 class。React 在渲染时会自动把 className 转换为 HTML 的 class 属性。同理,for → htmlFor。Q JSX 中的 style 为什么是双花括号
{{}}?A 外层花括号表示"在 JSX 中嵌入 JavaScript 表达式",内层花括号表示"这是一个 JavaScript 对象"。所以
style={{ color: 'red' }} 的意思是:传递一个 { color: 'red' } 的 JS 对象作为 style 属性的值。Q 项目中的 .tsx 和 .ts 文件有什么区别?
A
.tsx 文件可以包含 JSX 语法(即可以在 TypeScript 中写 HTML 标签),而 .ts 文件不能。所有包含 React 组件的文件都应该是 .tsx 后缀。纯逻辑文件(如工具函数、类型定义)可以用 .ts 后缀。Q Vite 和 Webpack 有什么区别?为什么教程选 Vite?
A Vite 用原生 ES Module 做开发服务器,冷启动毫秒级;Webpack 需要打包整个应用再启动,项目越大越慢。Vite 的 HMR(热更新)也更快,因为只需要替换修改的模块。生产构建两者都用 Rollup/esbuild 打包,性能差距不大。教程选 Vite 是因为开发体验更好,也是 React 社区的新趋势。
📖 小节
- 使用
npm create vite@latest my-app -- --template react-ts快速搭建 React + TypeScript 项目 - JSX 不是 HTML,而是 JavaScript 语法扩展:用
className替代class,用{}嵌入表达式 - 每个 JSX 表达式必须有一个根元素(可以用
<></>Fragment) - Vite 比 CRA 快 10 倍以上,本教程全程使用 Vite
- React DevTools 是必备调试工具,可以查看组件树和状态
📝 作业
- 基础题(难度⭐):用 Vite 创建一个新的 React + TypeScript 项目,修改
App.tsx显示你的名字和一句话自我介绍。 - 练习(难度⭐⭐):在 App 组件中创建一个变量
const isLoggedIn = true,在 JSX 中使用三元运算符显示「欢迎回来」或「请登录」。 - 探究(难度⭐):打开 React DevTools 的 Components 面板,查看 Vite 默认模板的组件树结构,截图保存。