"在 JS 里写 HTML?这是什么操作?" 别慌!今天从最基础的 JSX 语法讲到 React 组件开发,用 "HTML+JS" 的混合思维带你入门,看完就能写出第一个 React 组件~
🔍 JSX本质:JavaScript的超能力扩展
JSX = JavaScript + XML,它允许我们在JavaScript中直接编写类似HTML的模板结构。它不是标准 JS,而是JS的扩展语法,所以需要 Babel 等工具解析后才能运行。

JSX 的核心规则:用{}
在 HTML 结构中嵌入 JS 代码
-
字符串传递 :
普通 HTML 里写字符串用引号,JSX 里如果想动态传字符串(或用 JS 变量拼字符串),可以这样:
jsxfunction App() { return <h1>Hello, 世界!</h1>; // 渲染出 <h1>Hello, 世界!</h1> }
-
识别 JS 变量 :
把 JS 变量的值嵌入 JSX 结构,直接用
{变量名}
。比如:jsxconst num = 10; function App() { return <p>这是一个数字:{num}</p>; // 显示 这是一个数字:10 }
-
函数 / 方法调用 :
想在 JSX 里执行函数并展示结果,直接调用:
jsxfunction add(a, b) { return a + b; } function App() { return <p>计算结果:{add(3, 5)}</p>; // 显示 计算结果:8 }
-
使用 JS 对象 :
比如给元素加样式对象,或者传递复杂数据:
jsxconst styleObj = { color: 'red', fontSize: '20px' }; function App() { return <p style={styleObj}>我是红色、20px 的文字</p>; }
JSX 的 "小脾气":和 HTML 的区别
JSX 虽然长得像 HTML,但有几个细节不一样:
- class → className :因为
class
是 JS 的关键字,所以 JSX 里用className
定义类名; - style 用对象 :内联样式不能直接写字符串,要写成 JS 对象(键是驼峰命名,比如
fontSize
); - 标签必须闭合 :单标签(如
<img>
、<input>
)必须写成<img />
,双标签必须成对(如<div></div>
)。
jsx
// 正确的JSX写法
const element = (
<div className="box" style={{ color: "red", fontSize: "16px" }}>
<img src="logo.png" alt="logo" /> {/* 单标签必须闭合 */}
<input type="text" />
</div>
);
列表渲染:map 与 key 的 "默契配合"
开发中经常要渲染列表(比如数组转成一组 li
),用 map
遍历数组,同时必须加独一无二的 key (React 内部用来高效更新 DOM 的)。
示例:渲染 todo 列表
jsx
const todos = ['吃饭', '睡觉', '打代码'];
function App() {
return (
<ul>
{todos.map((todo, index) => (
// 简单场景用 index 当 key(数据不变、无删除等操作时可用),复杂场景最好用唯一 ID
<li key={index}>{todo}</li>
))}
</ul>
);
}
为什么要 key ?React 靠 key 识别哪些元素新增、删除、变动,没 key 可能导致渲染异常、性能变差,面试也超爱考这点!
条件渲染:在 JSX 里写 "如果... 就..."
JSX 里不能直接写if
语句,但可以用这 3 种方式实现条件渲染:
1. &&
运算符:满足条件才显示
jsx
const isLogin = true;
const element = (
<div>
<p>首页</p>
{isLogin && <p>欢迎回来,用户!</p>} {/* isLogin为true时显示 */}
</div>
);
2. ?:
三元运算符:二选一
jsx
const score = 85;
const element = (
<div>
{score >= 60 ? (
<p>及格了!</p>
) : (
<p>不及格,继续努力~</p>
)}
</div>
);
3. 自定义函数 + if
条件太多时,把逻辑抽到函数里,JSX 会更简洁:
jsx
function getGrade(score) {
if (score >= 90) return '优秀';
if (score >= 60) return '合格';
return '不合格';
}
function App() {
const score = 85;
return <p>你的评级:{getGrade(score)}</p>;
}
React 事件处理:让交互 "活" 起来
1.事件绑定:on + 事件名称 = {事件处理程序}
React 里绑定事件,语法是 on+事件名
(注意驼峰命名,比如 onClick
、onChange
),值是一个函数。示例:点击按钮提示消息
jsx
function App() {
function handleClick() {
alert('按钮被点击啦!');
}
return <button onClick={handleClick}>点我</button>;
// 注意:是 onClick={handleClick} ,不是 onClick="handleClick()" ,后者是字符串,不会执行函数!
}
2.事件对象与自定义参数
-
事件对象 e:React 会自动传递事件对象,比如阻止默认行为:
jsxfunction handleLinkClick(e) { e.preventDefault(); // 阻止链接默认跳转 alert('链接被点击,但没跳转~'); } function App() { return <a href="https://baidu.com" onClick={handleLinkClick}>点我试试</a>; }
-
传递自定义参数:需要用箭头函数 "包裹",比如点击按钮传递 ID:
jsxfunction handleBtnClick(id) { alert(`你点击了 ID 为 ${id} 的按钮`); } function App() { return ( <button onClick={() => handleBtnClick(10)}> 点我传参 </button> ); }
这里用箭头函数,是为了让
handleBtnClick
能拿到自定义参数10
,否则直接写onClick={handleBtnClick(10)}
会导致函数直接执行(页面渲染时就触发),而不是点击时触发。
🧩 组件化:构建React应用的基石
组件是 React 的核心,首字母大写的函数,支持嵌套和复用
jsx
// 这是一个组件,负责显示标题
function Title() {
return <h1>我是页面标题组件</h1>;
}
// 这是另一个组件,用来组合其他组件
function App() {
return (
<div>
<Title /> {/* 组件复用:这里渲染 Title 组件 */}
<p>这是页面内容...</p>
</div>
);
}
组件首字母大写是约定,React 靠这个区分 "普通 HTML 标签" 和 "自定义组件",小写的会被当普通标签处理,这点面试常考!
组件渲染:自闭和与成对标签
组件渲染时,写法和 HTML 标签一样,可以是自闭和(/<组件名 />
)或成对( <组件名></组件名>
)。比如:
jsx
function Button() {
return <button>我是按钮组件</button>;
}
function App() {
return (
<div>
<Button /> {/* 自闭和写法 */}
<Button></Button> {/* 成对写法,效果一样 */}
</div>
);
}
如果组件内部有子内容(比如 div
里套其他元素),就用成对写法;单纯复用组件,自闭和更简洁。
状态管理:useState 让组件 "有记忆"
React 组件默认是 "无状态" 的 ------ 每次渲染都会重置变量。useState
是 React 提供的 "状态钩子",能让组件记住数据(状态),并在数据变化时自动重新渲染。
1. 基本用法:定义和使用状态
jsx
import { useState } from 'react'; // 导入useState
function Counter() {
// 定义状态:count是当前值,setCount是更新函数
const [count, setCount] = useState(0); // 初始值为0
return (
<div>
<p>点击了{count}次</p>
<button onClick={() => setCount(count + 1)}>
点我+1
</button>
</div>
);
}
useState(0)
:初始化状态为 0,返回一个数组[当前值, 更新函数]
;setCount(count + 1)
:更新状态(必须用更新函数,不能直接修改 count,因为状态是只读的);- 调用
setCount
后,组件会重新渲染,count
会显示新值。
2. 状态是 "替换" 不是 "修改"
React 状态是只读的,更新对象 / 数组时,必须返回新的对象 / 数组,不能直接修改原数据:
jsx
// 错误:直接修改状态(不会触发重新渲染)
const [user, setUser] = useState({ name: "张三" });
// ❌ user.name = "李四";
// 正确:返回新对象
setUser({ ...user, name: "李四" }); // 用扩展运算符复制旧数据,再修改
// 数组同理:不能直接push,要返回新数组
const [list, setList] = useState([1, 2, 3]);
setList([...list, 4]); // 正确:添加新元素并返回新数组
受控组件:表单输入的数据 "由 React 管"
在 React 中,表单元素(如 input、select)的值最好由状态管理,这样数据和视图能保持同步(类似 Vue 的v-model
)。
jsx
import { useState } from 'react';
function InputDemo() {
// 用状态管理输入值
const [text, setText] = useState("");
return (
<input
type="text"
value={text} // 输入框的值由状态控制
onChange={(e) => setText(e.target.value)} // 输入变化时更新状态
/>
);
}
value={text}
:输入框显示状态text
的值;onChange
:输入变化时,用setText
更新状态;- 这样输入框的值始终和
text
一致,称为 "受控组件"。
🎯获取 DOM:useRef 让你直接 "摸" 到元素
有时候需要直接操作 DOM(如获取输入框焦点),可以用useRef
钩子:
jsx
import { useRef, useEffect } from 'react';
function RefDemo() {
// 创建ref对象
const inputRef = useRef(null);
// 组件渲染后,让输入框自动聚焦
useEffect(() => {
inputRef.current.focus(); // current指向DOM元素
}, []);
return (
<input
type="text"
ref={inputRef} // 绑定ref到输入框
/>
);
}
useRef(null)
创建一个 ref 对象,inputRef.current
会指向绑定的 DOM 元素;- 适合处理焦点、获取画布上下文等需要直接操作 DOM 的场景。