React 基础
约 2711 字大约 9 分钟
2025-10-16
简介
React 是一个用于构建用户界面的开源 JavaScript 库。起初由 Facebook 的软件工程师 Jordan Walke 创建,并于 2013 年 5 月宣布开源。
在早期 web 界面使用 JS 或者 JQuery 的来构建用户界面。这种方式需要一步步更改代码操作 DOM 来改变页面:找到 DOM 节点、更改样式、修改内容、还要考虑到数据的状态,这叫做命令式编程。
React 的核心思想可以被精炼为一个优雅的公式:UI = f(State)。
这个公式的含义是:用于界面仅仅是应用程序状态的一个函数。使用 React 构建页面,不需要再考虑如何在各种数据变化下一步步操作 DOM 来修改页面,只需要描述在特定状态下,界面是什么样子。 React 能够高效的比较差异,更改真实 DOM 中需要变化的部分。这被称作声明式编程,其优点相比命令式编程是显而易见的。
JSX
简介
JSX 全称 JavaScript XML, 是一种类似于 XML 的 JavaScript 的语法扩展,本质是 React.createElement(...) 的语法糖。 React 官方推荐使用 JSX 来描述 UI 结构。
在 React 17 之前,JSX 需要通过 Babel 等构建工具编译为 React.createElement(...) 调用,并且组件文件中必须显式导入 React。 但从 React 17 开始,React 引入了新的 JSX 转换,不再依赖 React.createElement,也不再强制要求导入 React。
例如,以下 JSX 代码:
// JSX 代码
export default function App() {
return (
<header>
<h1 style={{ color: "red" }}> Hello, React! </h1>
</header>
);
}在启用了新 JSX 转换后,会被 Babel 编译为类似这样的 JavaScript:
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
export default function App() {
return _jsxs("header", {
children: _jsx("h1", {
style: { color: "red" },
children: "Hello, React!",
}),
});
}这里使用的 _jsx 和 _jsxs(用于带多个子元素的情况)来自 react/jsx-runtime,是 React 内部提供的轻量运行时函数。开发者无需手动导入它们——构建工具会自动处理。
这些函数会创建 React 元素对象,然后由 ReactDOM(或 React Native 等渲染器)将这些对象转换为真实的 DOM 节点并插入页面。 最终,用户在浏览器中看到的是标准的 HTML:
<header>
<h1 style="color: red;">Hello, React!</h1>
</header>因此,尽管底层实现发生了变化,JSX 依然是对 React 元素创建过程的一种简洁、直观的语法封装——本质上,它仍然是“语法糖”,只是在 React 17+ 中变得更轻量、更自动化了。
规则
JSX 语法可以像 HTML 一样,直接在 JS 中描述标签,但需要遵循一些规则。
1. 只能返回一个根元素
如果想要在一个组件中包含多个元素,需要用 一个 父标签把它们包裹起来。父标签可以是 <div>、<button> 等,也可以用 <> ... </> 元素来代替:
export default function TodoList() {
return (
<>
<h1>Todo List</h1>
</>
);
}
// 也可以这样写
export default function TodoList() {
return <h1>Todo List</h1>
}2. 标签必须闭合
JSX 中的标签必须要有开始标签和结束标签,若是自闭和标签则必须用 /> 来闭合:
export default function Profile() {
return (
<div>
<h1>React</h1>
<img src="logo.png" alt="react logo" />
</div>
);
}3. 驼峰命名法
在 React 中,大部分 HTML 和 SVG 属性都用驼峰式命名法表示:
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
/>4. 内联样式使用对象
组件样式推荐使用内联样式,能够让每个组件都包含组件的所有代码。
style={{color:'skyblue',fontSize:'24px'}}5. 标签混入 JS 表达式 时要用 {}
<div>
<p>{a + b}</p>
</div>注意
{} 中能够写入的是 JS 表达式,而不是 JS 语句。
表达式是能计算出一个值的代码片段,例如:
{
2 + 3;
} // 返回 5
{
x * y;
} // 返回 x 和 y 的乘积
{
("hello");
} // 字符串字面量,返回 "hello"
{
foo();
} // 函数调用表达式,返回函数的返回值
{
[1, 2, 3];
} // 数组字面量,返回 123
x > 0 ? "pos" : "neg"; // 三元表达式语句是执行某种操作的完整指令,不直接产生值。例如用于控制程序流程、声明变量、定义函数等。
let x = 5; // 变量声明语句
if (x > 0) { ... } // 条件语句
for (let i = 0; i < 10; i++) { ... } // 循环语句
function foo() {} // 函数声明语句
return x; // return 语句组件
React 应用是由被称为 组件 的独立 UI 片段构建而成。组件是用来实现局部功能的代码和资源的集合。组件化可以复用代码,提高开发与运行效率。
定义组件
- 类组件
使用 ES6 class 继承 React.Component:
class Counter extends React.Component {
// 构造器函数
constructor(props) {
super(props);
// this 指向 Counter 实例对象
this.state = { count: 0 };
}
// 供实例使用的方法
increment = () => {
this.setState({ count: this.state.count + 1 });
};
// render() 必须写,且要有返回值
render() {
return (
<div>
<p>{this.state.count}</p>
<button onClick={this.increment}>+</button>
</div>
);
}
}- 函数组件
函数组件是一个 JavaScript 函数,当我们把函数名称大写时,React 会将此函数视为组件。
import { useState } from "react"; // 引入 useState Hooks
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}从上面的代码可以看出来函数式组件更简洁,逻辑复用更简单。类式组件是 React 16.8 版本之前创建组件的方式。
React 16.8(2019)推出了 hooks API,函数组件成为与类组件平级的主流开发方式,而后官方持续强化函数组件生态,如今 函数组件 + Hooks 已成为主流开发范式。 React 团队也明确表示:未来不会为类组件添加新功能,函数组件是推荐的编写方式。
| 特性 | 类式组件 | 函数式组件 |
|---|---|---|
| 语法简洁性 | 冗长,需处理 this 绑定 | 简洁、直观,无 this 问题 |
| 状态管理 | this.state + this.setState() | useState / useReducer |
| 逻辑复用 | 需借助 HOC 或 render props,较复杂 | 自定义 Hooks,组合性强、复用简单 |
| 性能优化 | shouldComponentUpdate 或 PureComponent | React.memo、useMemo、useCallback |
| React 官方推荐 | ❌ 已不再推荐新项目使用 | ✅ 当前及未来推荐的方式 |
| 新特性支持(React 18+) | 不支持(如并发渲染、新 Hooks) | 完全支持 |
方式对比
导入导出组件
React 中组件的导入和导出方式与 JavaScript 的导入导出方式 一样:
import Profile from "./Profile.js";
// 或者
import Profile from "./Profile";
export default function App() {
return <Profile />;
}Props
React 组件通过 Props 来传递数据。父组件通过属性向子组件传递数据,子组件通过形参接收数据。在子组件中,不要修改 props。
export default function App() {
let name = "React";
return (
<Profile name={name} desc="用于构建 Web 和原生交互界面的库" />
);
}export default function Profile(props) {
return (
<div>
<h1>{props.name}</h1>
<p>{props.desc}</p>
</div>
);
}在子组件中,可以对 props 进行解构和设置默认值:
export default function Profile({ name = "React", desc }) {
return (
<div>
<h1>{name}</h1>
<p>{desc}</p>
</div>
);
}props 还可以传递组件的子内容。它与 Vue 中的插槽 一样, 用于将父组件的内容“透传”到子组件的指定位置,实现组件的灵活组合。
function Parent() {
return (
<Children title="This is title">
<p>This is the text</p>
<button>Click me</button>
</Children>
);
}function Children({ children, title }) {
return (
<div className="card">
{title && <h2>{title}</h2>}
<div className="card-body">{children}</div>
</div>
);
}<Children /> 传入的子内容会通过 children 属性传递给子组件。并在子组件中 {children} 的位置进行渲染。
条件渲染
在 Vue 中,我们使用 v-if 、 v-else 和 v-else-if 指令来实现条件渲染。而在 React 中, 我们使用 JavaScript 中的 if 语句、&& 和 ? : 运算符来选择性地渲染 JSX。
export default function App() {
const isOpen = true;
if (isOpen) {
return <div>Open</div>;
} else {
return <div>Close</div>;
}
}使用三目运算符能够大大减少代码量:
export default function App() {
const isOpen = true;
return <div>{isOpen ? "Open" : "Close"}</div>;
}如果我们想在条件成立时渲染一些 JSX,或者不做任何渲染,可以使用逻辑与运算符 &&:
export default function App() {
const did = true;
return <div>学习 React{did && "✅"}</div>;
}当我们的判断更加复杂,可以 选择性地将 JSX 赋值给变量,这种方式代码更冗长,但更加灵活。
export default function App() {
const did = true;
let content;
if (did) {
content = <div>学习 React✅</div>;
}
return <div>{content}</div>;
}列表渲染
在 Vue 中,我们可以在标签上使用 v-for 指令来实现列表渲染。而在 React 中, 我们是通过 JavaScript 的数组方法来操作数组中的数据。例如使用 filter() 筛选需要渲染的组件、使用 map() 把数组转换成组件数组。
export default function List() {
const people = [
{ id: 0, name: "Max", profession: "数学家" },
{ id: 1, name: "Nores", profession: "物理学家" },
];
const listItems = people.map((person) => <li key={person.id}> ... </li>);
return <ul>{listItems}</ul>;
}提示
直接放在 map() 方法里的 JSX 元素一般都需要指定 key 值,key 属性应该保持不变。
事件处理函数
事件处理函数
在 React 中,我们可以使用 onClick、onChange、onSubmit 等属性来监听事件。
处理函数通常在组件内定义,名称约定以 handle 开头,后跟事件名称,将其作为 Props 传入。
export default function Button() {
function handleClick() {
alert("You clicked!");
}
return <button onClick={handleClick}> Click </button>;
}若事件处理函数需要接收参数,应该使用 {} 包起来:
function AlertButton({ message, children }) {
return <button onClick={() => alert(message)}>{children}</button>;
}export default function Toolbar() {
return (
<div>
<AlertButton message="Upload image">Upload iamge</AlertButton>
</div>
);
}注意
传递给事件处理函数的函数应直接传递,而非调用。
| (传递一个函数)正确 | (调用一个函数)错误 |
|---|---|
<button onClick={handleClick}> | <button onClick={handleClick()}> |
<button onClick={() => alert('...')}> | <button onClick={alert('...')}> |
事件处理函数作为 Props 传递时,应该直接传递,如果有参数应该使用箭头函数。
若是直接调用,函数会在每次组件渲染时触发,而非用户操作时触发。
阻止传播
事件处理函数会捕获来自任何子组件的事件,也就是事件会向父元素传播。
在 Vue 中,我们使用 .stop 修饰符来阻止事件传播,在 React 中,我们可以使用 event.stopPropagation() 来阻止事件传播。
function Button({ onClick, children }) {
return (
<button
onClick={(e) => {
e.stopPropagation();
onClick();
}}
>
{children}
</button>
);
}在上面的代码中,定义在 Button 组件中的事件处理函数会调用 e.stopPropagation() 来阻止事件冒泡,然后执行 onClick()。
阻止默认行为
在 Vue 中,我们可以使用 .prevent 修饰符来阻止默认行为,在 React 中,我们可以使用 event.preventDefault() 来阻止默认行为。
export default function Signup() {
return (
<form
onSubmit={(e) => {
e.preventDefault();
alert("提交表单!");
}}
>
<input />
<button>发送</button>
</form>
);
}Hooks
下一篇:Hooks