07 — React 是什么与 JSX
这一章是整个 React 学习的核心。
我会从问题出发讲:为什么要用 React → JSX 是什么 → 第一个组件。
1. 没有 React 时,你是怎么写网页的?
假设要做一个简单的「聊天输入框」。
1.1 纯 HTML + JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <div id="app"></div> <button id="btn">点击我</button> <p id="count">0</p>
<script> let count = 0; const btn = document.getElementById('btn'); const countEl = document.getElementById('count');
btn.addEventListener('click', () => { count++; countEl.textContent = count; }); </script>
|
问题:
- ❌ 命令式:每改一次数据,要手动写代码改 DOM
- ❌ 难维护:UI 复杂时,DOM 操作散落各处
- ❌ 难复用:想在不同地方复用同样 UI,得复制粘贴 HTML
- ❌ 没有组件概念:所有代码混在一个文件里
1.2 jQuery 时代(曾经的解决方案)
1
| $('#count').text(count);
|
但还是命令式,逻辑和 DOM 操作分离不干净。
1.3 React 的解法
核心转变:声明式 UI
1 2 3 4 5 6 7 8 9 10
| function App() { const [count, setCount] = useState(0);
return ( <div> <p>{count}</p> <button onClick={() => setCount(count + 1)}>+1</button> </div> ); }
|
你只声明「UI 应该长什么样」(数据 → UI 的映射),React 自动帮你更新 DOM。
2. React 是什么?(从外到内看)
2.1 三层定义
| 层级 | 定义 |
|---|
| 比喻 | UI 的 Excel 表格:你只改数据(state),UI 自动跟着变 |
| 功能 | 把「数据」渲染成「HTML」,并在数据变化时自动重新渲染 |
| 实质 | 一个 JavaScript 库(import React from 'react') |
2.2 React 的核心三个概念
1 2 3
| 1. 组件 (Component) — UI 的可复用单元 2. Props — 组件的输入参数 3. State — 组件内部会变化的数据
|
整本书就讲这三件事 + 它们的组合方式。
3. 第一个 React 程序(跟着写一遍)
3.1 创建 App.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import { useState } from 'react';
function App() { const [count, setCount] = useState(0);
return ( <div> <h1>计数器</h1> <p>当前数字:{count}</p> <button onClick={() => setCount(count + 1)}>+1</button> </div> ); }
export default App;
|
3.2 在 main.tsx 中挂载
1 2 3 4 5 6 7 8 9 10
| import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App';
ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <App /> </React.StrictMode>, );
|
3.3 发生了什么?
- 浏览器加载
index.html - 加载
main.tsx createRoot(...).render(<App />) 把 App 组件渲染到 <div id="root">- React 调用
App() 函数,得到返回的 JSX - React 把 JSX 转成真实 DOM,塞进
#root - 用户点按钮 →
setCount(count + 1) → 状态变了 - React 自动重新调用 App(),得到新的 JSX
- React 自动对比新旧 JSX,只更新变化的部分(这里只更新
<p> 的文字) - 浏览器看到新的 DOM
关键洞察:你只写了「数据怎么变」(setCount),没写「DOM 怎么变」。React 自动帮你做了。
4. JSX 是什么?
4.1 一句话
JSX 是「在 JavaScript 里写 HTML」的语法糖。
1 2
| const element = <h1>Hello, world!</h1>;
|
浏览器不认识 JSX。Vite/TypeScript 编译时把它转成 React.createElement(...) 调用:
1 2 3 4
| const element = React.createElement('h1', null, 'Hello, world!');
|
4.2 JSX vs HTML:5 个关键区别
区别 1:class → className
1 2 3 4 5
| <div class="container">
<div className="container">
|
区别 2:所有标签必须闭合
1 2 3 4 5 6 7 8 9
| <input type="text"> <br> <img src="...">
// JSX 里必须自闭合或成对闭合: <input type="text" /> <br /> <img src="..." />
|
区别 3:用 {} 插入 JavaScript 表达式
1 2 3 4 5 6 7 8 9
| const name = '豆豆'; const count = 3;
<div> <h1>{name}</h1> {/* 变量 */} <p>{count + 1}</p> {/* 表达式 */} <p>{count > 5 ? '大' : '小'}</p> {/* 条件 */} <p>{[1, 2, 3].map(x => x * 2)}</p> {/* 任意 JS 表达式 */} </div>
|
⚠️ {} 里只能放表达式,不能放语句(如 if、for、function() {...})。
1 2 3 4 5 6 7 8
| <div>{if (a) { return 'x' }}</div>
<div>{a ? 'x' : ''}</div>
<div>{(() => { if (a) return 'x'; return ''; })()}</div>
|
区别 4:事件名是驼峰式
1 2 3 4 5 6 7
| <button onclick="handleClick()">
<button onClick={handleClick}> <input onChange={(e) => setValue(e.target.value)} /> <form onSubmit={handleSubmit}>
|
区别 5:style 接受对象
1 2 3 4 5 6 7
| <div style="color: red; background: blue;">
<div style={{ color: 'red', background: 'blue' }}> // ↑↑ 外层 {} 是「插入 JS 表达式」 // ↑↑ 内层 {} 是对象字面量
|
4.3 JSX 必须有根节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| return ( <h1>标题</h1> <p>内容</p> );
return ( <div> <h1>标题</h1> <p>内容</p> </div> );
return ( <> <h1>标题</h1> <p>内容</p> </> );
|
项目里基本都用 Fragment <>...</>,避免无意义的 div 嵌套。
4.4 JSX 中的注释
5. React.createElement 是什么?(理解 JSX 的本质)
JSX 是语法糖,本质是 React.createElement(type, props, ...children)。
1 2 3
| <button onClick={handleClick} className="btn"> 点击 <strong>这里</strong> </button>
|
等价于:
1 2 3 4 5 6
| React.createElement( 'button', { onClick: handleClick, className: 'btn' }, '点击 ', React.createElement('strong', null, '这里'), );
|
返回的是一个普通对象(叫「React 元素」或「虚拟 DOM」):
1 2 3 4 5 6 7 8
| { type: 'button', props: { onClick: handleClick, className: 'btn', children: ['点击 ', { type: 'strong', props: { children: '这里' } }], }, }
|
React 维护了一棵这样的对象树(虚拟 DOM)。
当数据变化时,React 用 diff 算法对比新旧树,找出真正需要改动的部分,更新到真实 DOM。
6. 虚拟 DOM 是什么?(概念性了解)
虚拟 DOM = 用 JS 对象描述的「DOM 长什么样」。
1 2 3 4 5
| <button class="btn">点击</button>
{ type: 'button', props: { className: 'btn', children: '点击' } }
|
为什么需要虚拟 DOM?
直接操作真实 DOM 很慢(每次改动都触发浏览器重排/重绘)。
React 用虚拟 DOM 做中间层:
- 数据变了,生成新的虚拟 DOM 树
- 对比新旧树(diff 算法)
- 只把变化的部分应用到真实 DOM(最小化更新)
7. 项目里第一个组件:main.tsx + App.tsx
7.1 main.tsx — 应用入口
1 2 3 4 5 6 7 8 9 10 11 12
| import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import './styles/global.css'; import './styles/tokens.css'; import 'highlight.js/styles/github-dark.css';
ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> <App /> </React.StrictMode>, );
|
逐行解释:
import App from './App':导入根组件import './styles/global.css':导入全局 CSS(Vite 会处理)ReactDOM.createRoot(...):创建一个 React 根.render(<App />):把 App 组件渲染进去! 非空断言(告诉 TS root 元素一定存在)<React.StrictMode>:开发模式下的额外检查(生产环境无效果)
7.2 App.tsx — 根组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import { useEffect } from 'react'; import { Sidebar } from './components/Sidebar/Sidebar'; import { MessageList } from './components/MessageList/MessageList'; import { InputArea } from './components/InputArea/InputArea'; import { ConfirmDialog } from './components/ConfirmDialog/ConfirmDialog'; import { ConnectionError } from './components/ConnectionError/ConnectionError'; import { useChatStore } from './store/chatStore'; import styles from './App.module.css';
export default function App() { const testConnection = useChatStore((s) => s.testConnection); const loadChats = useChatStore((s) => s.loadChats); const createNewChat = useChatStore((s) => s.createNewChat);
useEffect(() => { (async () => { await testConnection(); await loadChats(); createNewChat(); })(); }, [testConnection, loadChats, createNewChat]);
return ( <> <div className="bg-blob bg-blob-1" /> <div className="bg-blob bg-blob-2" /> <div className={styles.app}> <Sidebar /> <div className={styles.main}> <div className={styles.top} /> <div className={styles.body}> <MessageList /> <InputArea /> </div> </div> </div> <ConfirmDialog /> <ConnectionError /> </> ); }
|
逐行解释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| import { useEffect } from 'react';
import { Sidebar } from './components/Sidebar/Sidebar';
import { useChatStore } from './store/chatStore';
import styles from './App.module.css';
export default function App() { const testConnection = useChatStore((s) => s.testConnection);
useEffect(() => { (async () => { await testConnection(); await loadChats(); createNewChat(); })(); }, [testConnection, loadChats, createNewChat]);
return ( <> <div className="bg-blob bg-blob-1" /> {/* 背景光晕 1 */} <div className="bg-blob bg-blob-2" /> {/* 背景光晕 2 */} <div className={styles.app}> {/* 应用容器 */} <Sidebar /> {/* 侧边栏 */} <div className={styles.main}> {/* 主区域 */} <div className={styles.top} /> {/* 顶部装饰条 */} <div className={styles.body}> {/* 主内容区 */} <MessageList /> {/* 消息列表 */} <InputArea /> {/* 输入区域 */} </div> </div> </div> <ConfirmDialog /> {/* 全局确认弹窗 */} <ConnectionError /> {/* 全局错误提示 */} </> ); }
|
8. 项目里所有 JSX 写法的「说明书」
| JSX 写法 | 含义 | 项目里例子 |
|---|
<div className="x"> | 普通元素 | <div className="bg-blob" /> |
<Component /> | 自闭合组件 | <Sidebar />、<MessageList /> |
<Component>...</Component> | 有子内容的组件 | (少) |
<div className={styles.x}> | CSS Module | <div className={styles.app}> |
<button onClick={fn}> | 点击事件 | <button onClick={createNewChat}> |
{condition && <X/>} | 条件渲染 | {isSending && <StopBtn />} |
{items.map(item => ...)} | 列表渲染 | {chatList.map(c => <ChatItem ... />)} |
{value} | 插入 JS | <span>{chat.title}</span> |
<>...</> | Fragment(无根节点) | App.tsx 的根 |
9. 一段话总结
React = 用 JS 写 UI、自动更新 DOM。
JSX = 在 JS 里写 HTML 的语法糖,编译成 React.createElement。
组件 = 返回 JSX 的函数。
核心思路:你只声明 UI 长什么样,数据变 → UI 自动更新。
接下来
你已经理解 React 的核心思想。接下来: