快速入门:React 核心概念全览
欢迎来到 React 的世界!本关将带你快速浏览你在日常开发中会用到的 80% 的 React 核心概念。如果你之前完全没有接触过前端框架,不用担心,我们会把每一个遇到的 HTML 与 JavaScript 知识点在当场为你讲透。
1. 什么是 React 组件?
在传统的网页制作中,HTML 负责结构,CSS 负责样式,JavaScript 负责交互。但随着网页变得越来越复杂,把一个按钮的结构写在 HTML 里、样式写在 CSS 里、点击逻辑写在 JS 里的做法会让维护变得极为痛苦。
React 改变了这种组织方式:它让你把界面的结构、外观和逻辑打包成一个个独立的、可复用的单元,这个单元就叫 组件(Component)。组件可以小到一个按钮、一个输入框,也可以大到整个导航栏、甚至整个页面。
定义你的第一个组件
在 React 中,组件本质上就是一个返回标签的普通 JavaScript 函数:
function MyButton() {
return (
<button>
我是一个按钮
</button>
);
}
HTML/JS 补课:
function MyButton():声明一个名为MyButton的函数。return (...):函数执行后返回括号里的内容。<button>...</button>:原生的 HTML 按钮标签。
嵌套并使用组件
声明完 MyButton 后,你可以把它像普通的 HTML 标签一样,嵌套在另一个组件中使用:
function MyButton() {
return (
<button>我是一个按钮</button>
);
}
export default function MyApp() {
return (
<div>
<h1>欢迎来到我的应用</h1>
<MyButton />
</div>
);
}
核心规则:组件名必须大写字母开头!
<button>(小写):React 知道这是原生 HTML 标签。<MyButton />(大写):React 知道这是你自己定义的 React 组件。如果写成<myButton />,React 会以为这是一个未知的 HTML 标签从而无法正常渲染。export default:JavaScript 的模块导出语法,表示这个函数是当前文件对外提供的主要组件。
2. 使用 JSX 编写标签
上面你在 JavaScript 代码中直接写出来的像 HTML 一样的标签,被称为 JSX。它是 React 的语法扩展,让你在写界面时拥有 HTML 的直观,同时又能使用 JavaScript 的全部编程能力。
JSX 比普通 HTML 更加严格,必须遵守以下核心规则:
- 必须返回单个根节点:组件不能同时返回两个并列的顶级标签。如果需要返回多个元素,必须用一个父标签包裹,或者用一个空的占位标签(称为 Fragment,写作
<>...</>):function AboutPage() { return ( <> <h1>关于我们</h1> <p>你好!<br />很高兴认识你。</p> </> ); } - 所有标签必须闭合:像
<br>或<img>这种在 HTML 里经常不闭合的单标签,在 JSX 中必须自闭合,写成<br />和<img />。
3. 添加样式:className
在 HTML 中,我们给元素指定样式类名使用的是 class="avatar"。但在 JavaScript 中,class 是一个保留关键字(用于声明 ES6 类)。因此,在 JSX 中为元素指定 CSS 类名时,必须使用 className:
<img className="avatar" />
然后在你的 CSS 文件中像往常一样编写样式规则:
.avatar {
border-radius: 50%;
}
4. 显示数据:进入 JavaScript 的花括号
JSX 允许你把标签写在 JavaScript 里面。而如果你想在标签里面反过来使用 JavaScript 的变量或表达式,就需要使用 花括号 {}。花括号就像一扇通往 JavaScript 世界的“传送门”:
const user = {
name: 'Hedy Lamarr',
imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
imageSize: 90,
};
export default function Profile() {
return (
<>
<h1>{user.name}</h1>
<img
className="avatar"
src={user.imageUrl}
alt={'Photo of ' + user.name}
style={{
width: user.imageSize,
height: user.imageSize
}}
/>
</>
);
}
为什么
style={{ ... }}有两层花括号?
- 外层的
{和}:告诉 JSX “这里要写一段 JavaScript 表达式”。- 内层的
{和}:表示一个普通的 JavaScript 对象字面量{ width: 90, height: 90 }。
5. 条件渲染与列表渲染
条件渲染
React 没有专门的条件标签指令(不像某些模板引擎)。在 React 中,你直接使用 JavaScript 的 if 语句或者三元运算符:
function AuthButton({ isLoggedIn }) {
if (isLoggedIn) {
return <button>登出</button>;
}
return <button>登录</button>;
}
或者用简洁的三元运算符写在 JSX 内部:
<div>
{isLoggedIn ? <AdminPanel /> : <LoginForm />}
</div>
列表渲染
要展示一个数据列表,可以使用 JavaScript 数组的 map() 方法将数据转换为标签列表。每一项都必须分配一个唯一的 key 属性:
const products = [
{ title: '卷心菜', id: 1 },
{ title: '大蒜', id: 2 },
{ title: '苹果', id: 3 },
];
export default function ShoppingList() {
const listItems = products.map(product =>
<li key={product.id}>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
}
为什么要给 key? key 就像每件商品的身份证号,当列表发生增删改排序时,React 依靠 key 精确判断哪个节点变了,避免重新销毁重建整个列表,保证性能与状态不混乱。
6. 响应事件与更新界面(State)
响应事件
在组件中声明一个事件处理函数,并通过 onClick 传递给按钮:
function MyButton() {
function handleClick() {
alert('你点击了我!');
}
return (
<button onClick={handleClick}>
点我
</button>
);
}
注意:onClick={handleClick} 结尾没有小括号!我们是在传递函数本身,而不是立刻调用它。
使用 useState 记住状态并更新界面
普通局部变量无法在两次点击之间保持住,修改普通变量也不会通知 React 重新渲染。要让组件“记住”信息并更新屏幕,我们需要使用 React 提供的 useState Hook:
import { useState } from 'react';
export default function MyApp() {
return (
<div>
<h1>独立的计数器</h1>
<MyButton />
<MyButton />
</div>
);
}
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
点击了 {count} 次
</button>
);
}
知识点解析:
const [count, setCount] = useState(0);:从 React 引入的函数只要以use开头就叫 Hook。它返回两个东西:当前状态值count,以及更新该状态的函数setCount。- 每个
<MyButton />实例拥有自己独立私有的状态。点击第一个按钮,只有第一个按钮的数字会加 1,第二个按钮毫无影响!
7. 在组件间共享数据:状态提升(Lifting State Up)
如果你希望上面两个按钮点击任意一个时,两个按钮的数字一起增加,该怎么办? 答案是:把状态从子组件中拿出来,“提升”到它们的公共父组件中,然后再通过 props 传递下去。
import { useState } from 'react';
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>共享状态的计数器</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
点击了 {count} 次
</button>
);
}
数据从 MyApp 自顶向下流向两个 MyButton,点击事件从子组件向上触发父组件的 setCount,父组件状态改变后重新渲染两个子组件。这就是 React 最强大的 单向数据流(One-way Data Flow)!
学习总结与能力自测
学完本关,你已经概览了 React 的核心运转方式:
- 组件是构建界面的大写 JavaScript 函数;
- JSX 遵循单根、自闭合与 className 规则;
- 花括号
{}负责在 JSX 中运行 JavaScript 表达式; - 列表渲染必须给兄弟节点分配唯一的
key; useState负责记住交互数据并驱动视图重新渲染;- 状态提升让多个子组件通过父组件共享数据。
在下一关中,我们将把这些概念全部运用起来,完整实现一个经典的井字棋游戏!