CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
前端入门实战/课时/React
RawBlame
课时目录12 节

前端入门实战

  1. 01HTML
  2. 02CSS
  3. 03JavaScript
  4. 04Git
  5. 05GitHub
  6. 06npm
  7. 07React
  8. 08Tailwind
  9. 09Vitest
  10. 10综合练习
  11. 11部署预览
  12. 12路线图对照

React

组件入门。

35 分钟

React:组件化开发入门

这一课你将能做到

  1. 用函数组件和 JSX 语法拆分页面为可复用的组件
  2. 用 props 传递数据,用 useState 管理组件内部状态
  3. 用 useEffect 处理副作用(请求、定时器),用条件渲染和列表渲染构建动态界面

为什么要学

原生 JS 操作 DOM 的问题是:代码和结构混在一起,数据变了要手动找元素、改内容,页面复杂后代码变成一团乱麻。React 的核心思想是「数据驱动视图」——你只描述「数据长什么样」,React 负责高效更新 DOM。

React 是目前全球最流行的前端框架,Meta(Facebook)出品,生态最成熟,岗位需求最大。学会 React 就拿到了现代前端开发的入场券。

先建立图像

React 的组件化思想:页面不是一整块 HTML,而是一棵组件树。

App(根组件)
├── Header
│   ├── Logo
│   └── Nav
├── Main
│   ├── ArticleList
│   │   ├── ArticleItem
│   │   └── ArticleItem
│   └── Sidebar
└── Footer

每个组件是一个函数,接收输入(props),返回描述界面的 JSX。数据变化时,React 自动重新渲染受影响的组件。

跟着做

步骤 1:创建 React 项目

用 Vite 创建现代 React 项目(官方推荐):

npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev

浏览器打开 http://localhost:5173,看到欢迎页面。项目结构:

my-react-app/
├── src/
│   ├── main.jsx      ← 入口,挂载 App
│   ├── App.jsx       ← 根组件
│   └── index.css
├── index.html
└── package.json

步骤 2:第一个函数组件

修改 src/App.jsx:

function App() {
    return (
        <div>
            <h1>我的 React 应用</h1>
            <p>这是第一个组件。</p>
        </div>
    );
}

export default App;

组件就是返回 JSX 的函数。JSX 看起来像 HTML,但其实是 JavaScript 语法糖,最终编译成 React.createElement() 调用。

步骤 3:JSX 语法规则

function App() {
    const name = "学习者";
    const isLoggedIn = true;
    const items = ["苹果", "香蕉", "橙子"];

    return (
        <div className="container">
            {/* 1. 插值表达式 */}
            <h1>你好,{name}</h1>

            {/* 2. 条件渲染:三元 */}
            <p>{isLoggedIn ? "欢迎回来" : "请登录"}</p>

            {/* 3. 条件渲染:与运算 */}
            {isLoggedIn && <button>退出</button>}

            {/* 4. 列表渲染:map + key */}
            <ul>
                {items.map((item, index) => (
                    <li key={index}>{item}</li>
                ))}
            </ul>

            {/* 5. 事件处理 */}
            <button onClick={() => alert("点击了")}>点我</button>

            {/* 6. class 要写成 className */}
            <div className="box">样式类</div>
        </div>
    );
}

JSX 规则:

  • 必须有一个根元素(或用 <> Fragment 包裹)
  • 所有标签必须闭合(<img />、<br />)
  • 属性用 camelCase:className、onClick、htmlFor
  • 插值用 {},里面可以是任何 JS 表达式

步骤 4:组件拆分与 props

把 UI 拆成独立组件,通过 props 传递数据:

// UserCard.jsx
function UserCard({ name, age, isVip }) {
    return (
        <div className="card">
            <h3>{name}</h3>
            <p>年龄:{age}</p>
            {isVip && <span className="badge">VIP</span>}
        </div>
    );
}

export default UserCard;
// App.jsx
import UserCard from "./UserCard";

function App() {
    const users = [
        { id: 1, name: "张三", age: 25, isVip: true },
        { id: 2, name: "李四", age: 30, isVip: false }
    ];

    return (
        <div>
            <h1>用户列表</h1>
            {users.map((user) => (
                <UserCard
                    key={user.id}
                    name={user.name}
                    age={user.age}
                    isVip={user.isVip}
                />
            ))}
        </div>
    );
}

props 是只读的,子组件不能修改父组件传下来的数据。这是 React 的单向数据流原则。

步骤 5:useState 管理状态

状态是组件内部会变化的数据。用 useState Hook 声明:

import { useState } from "react";

function Counter() {
    // 声明状态:count 是当前值,setCount 是更新函数
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>当前计数:{count}</p>
            <button onClick={() => setCount(count + 1)}>+1</button>
            <button onClick={() => setCount(0)}>重置</button>
        </div>
    );
}

关键点:

  • 不要直接修改状态(count++ 是错的),必须用 setCount
  • 状态更新是异步的,连续调用 setCount(count + 1) 两次可能只加一次;用函数式更新 setCount(prev => prev + 1) 更可靠
  • 状态变化会触发组件重新渲染

步骤 6:useEffect 处理副作用

副作用是组件渲染之外的操作:请求数据、设置定时器、操作 DOM:

import { useState, useEffect } from "react";

function UserList() {
    const [users, setUsers] = useState([]);
    const [loading, setLoading] = useState(true);

    // 组件挂载后执行一次
    useEffect(() => {
        fetch("https://jsonplaceholder.typicode.com/users")
            .then((res) => res.json())
            .then((data) => {
                setUsers(data);
                setLoading(false);
            });
    }, []); // 空依赖数组 = 只在挂载时执行

    if (loading) return <p>加载中...</p>;

    return (
        <ul>
            {users.map((user) => (
                <li key={user.id}>{user.name}</li>
            ))}
        </ul>
    );
}

useEffect 的依赖数组:

  • []:只在挂载时执行一次
  • [count]:count 变化时执行
  • 不写:每次渲染都执行(通常要避免)

步骤 7:表单处理

import { useState } from "react";

function LoginForm() {
    const [username, setUsername] = useState("");
    const [password, setPassword] = useState("");

    const handleSubmit = (e) => {
        e.preventDefault(); // 阻止表单默认刷新
        console.log("提交:", { username, password });
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
                type="text"
                value={username}
                onChange={(e) => setUsername(e.target.value)}
                placeholder="用户名"
            />
            <input
                type="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                placeholder="密码"
            />
            <button type="submit">登录</button>
        </form>
    );
}

React 表单采用「受控组件」模式:输入框的值由 state 控制,输入时通过 onChange 更新 state。

讲清楚要点

  1. 组件是函数,JSX 是返回值:React 组件就是普通的 JavaScript 函数,接收 props,返回 JSX。没有 class 的束缚,逻辑更直白。
  2. 数据驱动视图:不要手动操作 DOM,而是更新 state,让 React 自动重新渲染。你写「数据长什么样」,React 管「怎么更新页面」。
  3. 单向数据流:数据从父组件通过 props 流向子组件,子组件通过回调函数把事件传回父组件。禁止子组件直接改 props。
  4. 状态是不可变的:更新对象/数组时要创建新副本,不要原地修改。setUsers([...users, newUser]) 而不是 users.push(newUser)。
  5. Hook 是 React 的灵魂:useState 管状态,useEffect 管副作用,useContext 管全局数据。函数组件 + Hook 是现代 React 的唯一写法。

你可能会懵的地方

  • 为什么 class 要写成 className? 因为 JSX 最终编译成 JavaScript,class 是 JS 的保留关键字。for 同理要写成 htmlFor。

  • 为什么列表要加 key? key 帮助 React 识别哪些元素变了、新增、删除。用数组索引做 key 在列表会重排时会导致状态错乱,优先用唯一 ID。

  • 为什么我的状态更新了但页面没变化? 两种可能:1) 直接修改了状态对象而不是用 set 函数;2) 修改的是同一个引用(比如 users.push()),React 的浅比较检测不到变化。

  • useEffect 里的空依赖数组到底是什么意思? 告诉 React:这个副作用不依赖任何 props 或 state,只在组件挂载时执行一次。如果不写,每次渲染都会执行,可能导致无限循环。

  • React 和 Vue/Angular 有什么区别? React 是「库」,只负责视图层,路由、状态管理需要额外配;Vue/Angular 是「框架」,全家桶开箱即用。React 更灵活,生态最大;Vue 上手更简单。

错法对照

错法问题正解
count++ 直接改 stateReact 检测不到变化,不重新渲染setCount(count + 1)
users.push(newUser)原地修改数组,引用不变setUsers([...users, newUser])
JSX 里写 class=编译报错className=
组件名小写 <userCard />React 当成 HTML 标签组件名必须大写开头
在 JSX 里写 if 语句JSX 只能写表达式用三元或逻辑与
useEffect 里写 async 函数useEffect 不支持 async 回调内部定义 async 函数再调用
事件写 onClick="fn()"字符串不会执行onClick={fn} 或 onClick={() => fn()}
列表不加 keyReact 警告,状态可能错乱加唯一稳定的 key

卡点急救

  1. 页面白屏,控制台报错:看报错信息,通常是 JSX 语法错误(标签没闭合、属性写错)或组件名没大写。
  2. 状态更新了但视图没变:检查是否直接修改了 state;检查 key 是否唯一;检查是否用了异步更新后的旧值。
  3. useEffect 无限循环:依赖数组里放了每次渲染都新建的对象/函数,导致 effect 反复执行。用 useMemo/useCallback 缓存,或把对象移到组件外。
  4. 跨组件传递数据太麻烦:中间组件只是透传 props 时,考虑用 useContext 或状态管理库(Zustand/Redux)。

面试常问

  • 什么是虚拟 DOM? React 在内存中维护一个轻量的 JS 对象树(虚拟 DOM),数据变化时先对比新旧虚拟 DOM(Diff 算法),计算出最小 DOM 操作,再批量更新真实 DOM。这比直接操作 DOM 快得多。

  • 函数组件和 class 组件的区别? 函数组件更简洁,配合 Hook 可以完全替代 class 组件。class 组件有生命周期方法(componentDidMount 等),函数组件用 useEffect 模拟。现代 React 只用函数组件。

  • key 的作用? key 是 React Diff 算法的标识,帮助 React 识别哪些元素是同一个。key 稳定且唯一时,React 能高效复用 DOM 节点;key 用索引且列表会重排时,可能导致状态错乱和性能问题。

  • 受控组件和非受控组件? 受控组件:表单值由 React state 控制,通过 onChange 更新;非受控组件:表单值由 DOM 自身管理,通过 ref 读取。推荐受控组件,便于校验和联动。

自测三问

  1. 写一个 Counter 组件,包含加一、减一、重置三个按钮。
  2. 为什么不能直接修改 state(如 user.name = "新名字")?正确的写法是什么?
  3. useEffect(() => {...}, []) 和 useEffect(() => {...}) 的区别是什么?

小结一句话

React 用组件拆分界面、用 state 驱动更新、用 Hook 管理逻辑——数据一变,视图自动跟着变,你只管描述 UI 长什么样。

延伸

  • React 官方文档(中文)
  • 下一课:Tailwind CSS——用工具类快速构建现代界面。
加载本节练习…
← 上一课npm下一课 →Tailwind