0%

已完成 0/29 关

教学模式

你可以随时调整。

此设置调整教师的教学方式,无需重新生成课程。

第 1 关 含代码 约 20 分钟

快速入门:React 核心概念全览

快速入门: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 更加严格,必须遵守以下核心规则:

  1. 必须返回单个根节点:组件不能同时返回两个并列的顶级标签。如果需要返回多个元素,必须用一个父标签包裹,或者用一个空的占位标签(称为 Fragment,写作 <>...</>):
    function AboutPage() {
      return (
        <>
          <h1>关于我们</h1>
          <p>你好!<br />很高兴认识你。</p>
        </>
      );
    }
  2. 所有标签必须闭合:像 <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>
  );
}

知识点解析:

  1. const [count, setCount] = useState(0);:从 React 引入的函数只要以 use 开头就叫 Hook。它返回两个东西:当前状态值 count,以及更新该状态的函数 setCount。
  2. 每个 <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 的核心运转方式:

  1. 组件是构建界面的大写 JavaScript 函数;
  2. JSX 遵循单根、自闭合与 className 规则;
  3. 花括号 {} 负责在 JSX 中运行 JavaScript 表达式;
  4. 列表渲染必须给兄弟节点分配唯一的 key;
  5. useState 负责记住交互数据并驱动视图重新渲染;
  6. 状态提升让多个子组件通过父组件共享数据。

在下一关中,我们将把这些概念全部运用起来,完整实现一个经典的井字棋游戏!

下一站:教程:从零完整实现井字棋游戏与时间旅行 →
© 2026 React 官方教程