React: 环境搭建与 JSX

最后更新:2026-08-26

写 React 代码就像装修房子——Vite 是施工队(帮你搭建脚手架和开发服务器),JSX 是装修图纸(一种在 JavaScript 里写 HTML 的语法)。这一课你会亲手搭建第一个 React 项目。


1. 你将学到



2. 一个新手开发者的真实故事

(1) 痛点:搭建环境花了一整天

Bob 刚学会 JavaScript,想试试 React。他在网上搜教程,有人推荐 Create React App,有人推荐 Vite,还有人推荐 Next.js。他随便选了 CRA 开始安装:

BASH
npx create-react-app my-app

结果:

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() 调用。

100%
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),会出现 ComponentsProfiler 两个新标签页:

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
逻辑代码 55 行(超过 40 行限制,仅展示)

❓ 常见问题

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 viteyarn create vite
Q JSX 里为什么不能用 class,要用 className?
A 因为 class 是 JavaScript 的保留关键字。JSX 本质是 JavaScript,所以不能使用 class。React 在渲染时会自动把 className 转换为 HTML 的 class 属性。同理,forhtmlFor
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 社区的新趋势。

📖 小节


📝 作业

  1. 基础题(难度⭐):用 Vite 创建一个新的 React + TypeScript 项目,修改 App.tsx 显示你的名字和一句话自我介绍。
  2. 练习(难度⭐⭐):在 App 组件中创建一个变量 const isLoggedIn = true,在 JSX 中使用三元运算符显示「欢迎回来」或「请登录」。
  3. 探究(难度⭐):打开 React DevTools 的 Components 面板,查看 Vite 默认模板的组件树结构,截图保存。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏