React

分类: 前端

React教程

目录

  1. React简介
  2. 环境搭建
  3. JSX语法
  4. 组件基础
  5. Props与State
  6. 生命周期与Hooks
  7. 事件处理
  8. 条件渲染与列表渲染
  9. 表单处理
  10. 组件通信
  11. React Router路由
  12. 状态管理
  13. Hooks深入
  14. 最佳实践

一、React简介

1.1 什么是React

React是由Facebook开发的用于构建用户界面的JavaScript库。它采用了组件化的开发思想,允许开发者将复杂的UI拆分成独立、可复用的代码片段,每个组件维护自己的状态和逻辑。React的核心特点包括虚拟DOM、单向数据流、声明式编程等,这些特性使其成为现代Web开发中最受欢迎的前端框架之一。

React最初由Jordan Walke在2013年开源,最初用于Facebook的News Feed产品。经过多年的发展,React已经成为前端开发领域的主流技术,被广泛应用于各种规模的Web应用中。许多知名公司如Netflix、Airbnb、Uber、Twitter等都采用React构建其产品。

React的主要优势在于其高效的渲染机制和灵活的组件化架构。虚拟DOM技术使得React能够在不直接操作真实DOM的情况下,通过对比前后状态的差异来最小化DOM操作,从而大幅提升应用性能。组件化的开发模式则提高了代码的可维护性和可测试性。

1.2 React核心概念

概念 说明
组件(Component) UI的独立可复用构建块
JSX JavaScript的语法扩展,用于描述UI
Props 父组件向子组件传递数据
State 组件内部的响应式数据
虚拟DOM 内存中的DOM表示
单向数据流 数据从父到子的流向

1.3 React与其他框架对比

框架 特点 学习曲线 适用场景
React 灵活、组件化、生态丰富 中等 中大型应用
Vue 简单易学、文档友好 较平缓 中小型应用
Angular 完整解决方案、TypeScript优先 较陡 大型企业级应用

二、环境搭建

2.1 使用Create React App

Create React App是官方推荐的React项目创建工具,提供了开箱即用的开发环境。

# 安装Node.js(如果未安装)
# 访问 https://nodejs.org 下载安装

# 创建React应用
npx create-react-app my-app

# 进入项目目录
cd my-app

# 启动开发服务器
npm start

npx是npm提供的工具,会临时下载并执行create-react-app包,无需全局安装。项目创建完成后,会自动配置好Webpack、Babel、ESLint等开发工具。

2.2 使用Vite

Vite是新一代构建工具,相比Create React App具有更快的启动和热更新速度。

# 使用Vite创建React项目
npm create vite@latest my-react-app -- --template react

# 进入目录
cd my-react-app

# 安装依赖
npm install

# 启动开发服务器
npm run dev

Vite利用浏览器原生ES模块支持,实现了即时的服务器启动和热模块替换(HMR),在大型项目中性能优势明显。

2.3 项目结构

my-app/
├── public/              # 静态资源
│   ├── index.html
│   └── favicon.ico
├── src/                 # 源代码
│   ├── App.js          # 根组件
│   ├── App.css         # 组件样式
│   ├── index.js        # 入口文件
│   └── index.css       # 全局样式
├── package.json        # 项目配置
└── README.md           # 项目说明

三、JSX语法

3.1 什么是JSX

JSX是JavaScript的语法扩展,允许在JavaScript中编写类似HTML的标记。它看起来像HTML,但实际上是一种在JavaScript中描述UI结构的语法。JSX不是模板语言,它会被编译成普通的JavaScript函数调用。

// JSX示例
const element = (
    <div className="container">
        <h1>Hello, World!</h1>
        <p>Welcome to React</p>
    </div>
);

JSX中的元素必须闭合,自闭合标签需要以/>结尾。与HTML不同的是,JSX中使用className而不是class,因为class是JavaScript的保留字。

3.2 JSX表达式

JSX中可以嵌入任何JavaScript表达式,使用花括号包裹。

const name = "张三";
const age = 25;

// 嵌入变量
const element = (
    <div>
        <h1>姓名:{name}</h1>
        <h2>年龄:{age}</h2>
        <h3>两年后:{age + 2}</h3>
    </div>
);

// 嵌入函数调用
function formatName(user) {
    return user.firstName + " " + user.lastName;
}

const user = { firstName: "李", lastName: "四" };
const element = <h1>Hello, {formatName(user)}!</h1>;

3.3 JSX属性

// 字符串属性
const element = <div className="container" id="main"></div>;

// JavaScript表达式属性
const isActive = true;
const element = <button className={isActive ? "active" : "inactive"}>点击</button>;

// 展开属性
const props = { className: "btn", onClick: handleClick };
const element = <button {...props}>提交</button>;

3.4 JSX条件渲染

// if语句
function Greeting({ isLoggedIn }) {
    if (isLoggedIn) {
        return <h1>欢迎回来</h1>;
    }
    return <h1>请登录</h1>;
}

// 三元运算符
function LoginButton({ isLoggedIn }) {
    return (
        <button>
            {isLoggedIn ? "退出" : "登录"}
        </button>
    );
}

// 逻辑与运算符
function Warning({ hasWarning }) {
    return (
        <div>
            {hasWarning && <p className="warning">注意!</p>}
        </div>
    );
}

四、组件基础

4.1 函数组件

函数组件是现代React开发中推荐的方式,使用JavaScript函数定义组件。

// 简单的函数组件
function Welcome({ name }) {
    return <h1>Hello, {name}!</h1>;
}

// 箭头函数组件
const Greeting = ({ name }) => {
    return <h1>Hello, {name}!</h1>;
};

// 使用组件
function App() {
    return (
        <div>
            <Welcome name="张三" />
            <Welcome name="李四" />
        </div>
    );
}

函数组件本质是一个接收props对象并返回JSX的JavaScript函数。它们比类组件更简洁,现代React推荐优先使用函数组件配合Hooks。

4.2 类组件

类组件使用ES6的class语法,需要继承React.Component。

import React from "react";

class Welcome extends React.Component {
    render() {
        return <h1>Hello, {this.props.name}!</h1>;
    }
}

// 使用
function App() {
    return <Welcome name="张三" />;
}

类组件具有以下特点:需要继承React.Component、具有自己的state、具有生命周期方法、需要定义render方法返回JSX。

4.3 组件样式

// 内联样式
const style = {
    color: "blue",
    fontSize: "16px"
};

function StyledComponent() {
    return <p style={style}>内联样式</p>;
}

// CSS模块(文件名格式:Component.module.css)
// import styles from "./Button.module.css";
// <button className={styles.button}>点击</button>

// CSS-in-JS(styled-components库)
import styled from "styled-components";

const Button = styled.button`
    background: #4CAF50;
    color: white;
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;

    &:hover {
        background: #45a049;
    }
`;

function App() {
    return <Button>点击</Button>;
}

五、Props与State

5.1 Props

Props(属性)是父组件向子组件传递数据的机制。Props是只读的,子组件不应修改props的值。

// 父组件
function Parent() {
    return <Child name="张三" age={25} isActive={true} />;
}

// 子组件
function Child({ name, age, isActive }) {
    return (
        <div className={isActive ? "active" : "inactive"}>
            <h2>{name}</h2>
            <p>年龄:{age}</p>
        </div>
    );
}

// Props类型检查
import PropTypes from "prop-types";

function Child({ name, age }) {
    return <div>{name} - {age}</div>;
}

Child.propTypes = {
    name: PropTypes.string.isRequired,
    age: PropTypes.number
};

Child.defaultProps = {
    age: 18
};

5.2 State

State是组件内部的响应式数据,用于存储组件运行时可能变化的数据。与props不同,state是组件私有的,由组件自己管理。

import React, { useState } from "react";

function Counter() {
    // 声明state
    const [count, setCount] = useState(0);

    // 更新state
    const increment = () => {
        setCount(count + 1);
    };

    return (
        <div>
            <p>计数:{count}</p>
            <button onClick={increment}>增加</button>
        </div>
    );
}

useState是React Hook,返回一个状态值和更新该状态的函数。调用setCount会触发组件重新渲染,显示最新的计数值。

5.3 State与Props的区别

特性 State Props
所有者 组件自身 父组件
可变性 可变 只读
触发渲染 变化时重新渲染 变化时重新渲染
初始值 构造函数或useState 父组件传递

六、生命周期与Hooks

6.1 常用Hooks概述

React Hook是React 16.8引入的新特性,允许在函数组件中使用state和其他React特性。

Hook 用途
useState 管理组件状态
useEffect 处理副作用(数据获取、订阅等)
useContext 访问Context
useReducer 复杂状态管理
useRef 引用DOM或保存值
useMemo 缓存计算结果
useCallback 缓存函数

6.2 useEffect

useEffect用于处理副作用操作,类似于类组件的生命周期方法。

import React, { useState, useEffect } from "react";

function DataFetcher({ url }) {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        // 组件挂载时执行
        const fetchData = async () => {
            try {
                setLoading(true);
                const response = await fetch(url);
                const json = await response.json();
                setData(json);
            } catch (err) {
                setError(err.message);
            } finally {
                setLoading(false);
            }
        };

        fetchData();

        // 返回清理函数(组件卸载时执行)
        return () => {
            console.log("清理副作用");
        };
    }, [url]); // 依赖数组,url变化时重新执行

    if (loading) return <p>加载中...</p>;
    if (error) return <p>错误:{error}</p>;
    return <div>{JSON.stringify(data)}</div>;
}

useEffect接收两个参数:副作用函数和依赖数组。依赖数组控制effect何时重新执行,空数组表示只在挂载时执行一次,类似componentDidMount。

6.3 useRef

useRef返回一个可变的ref对象,其.current属性可以存储任意值。

import React, { useRef } from "react";

function TextInput() {
    const inputRef = useRef(null);

    const handleFocus = () => {
        // 聚焦输入框
        inputRef.current.focus();
    };

    const handleGetValue = () => {
        console.log(inputRef.current.value);
    };

    return (
        <div>
            <input ref={inputRef} type="text" />
            <button onClick={handleFocus}>聚焦</button>
            <button onClick={handleGetValue}>获取值</button>
        </div>
    );
}

useRef还常用于保存不需要触发重新渲染的值,比如计时器ID。


七、事件处理

7.1 绑定事件

function EventExample() {
    const handleClick = (event) => {
        console.log("点击事件", event);
    };

    const handleClickWithParam = (message) => {
        console.log(message);
    };

    return (
        <div>
            {/* 直接绑定 */}
            <button onClick={handleClick}>点击</button>

            {/* 传递额外参数 */}
            <button onClick={() => handleClickWithParam("Hello")}>带参数</button>

            {/* 传递事件对象和参数 */}
            <button onClick={(e) => handleClickWithParam("World")}>参数2</button>
        </div>
    );
}

7.2 常见事件处理

function FormEvents() {
    const handleSubmit = (e) => {
        e.preventDefault(); // 阻止表单默认提交
        console.log("表单提交");
    };

    const handleChange = (e) => {
        console.log("输入值:", e.target.value);
        console.log("输入名称:", e.target.name);
    };

    const handleFocus = (e) => {
        console.log("获得焦点");
    };

    const handleBlur = (e) => {
        console.log("失去焦点");
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
                type="text"
                name="username"
                onChange={handleChange}
                onFocus={handleFocus}
                onBlur={handleBlur}
            />
            <button type="submit">提交</button>
        </form>
    );
}

八、条件渲染与列表渲染

8.1 条件渲染

function UserProfile({ user, isLoading }) {
    // 加载状态
    if (isLoading) {
        return <div>加载中...</div>;
    }

    // 无用户
    if (!user) {
        return <div>请登录</div>;
    }

    // 正常显示
    return (
        <div>
            <h2>{user.name}</h2>
            <p>{user.email}</p>
        </div>
    );
}

// 使用&&进行条件渲染
function Warning({ hasWarning }) {
    return (
        <div>
            <h1>标题</h1>
            {hasWarning && <div className="warning">警告信息</div>}
        </div>
    );
}

8.2 列表渲染

function TodoList() {
    const [todos, setTodos] = useState([
        { id: 1, text: "学习React", completed: false },
        { id: 2, text: "做项目", completed: true },
        { id: 3, text: "写文档", completed: false }
    ]);

    const toggleTodo = (id) => {
        setTodos(todos.map(todo =>
            todo.id === id ? { ...todo, completed: !todo.completed } : todo
        ));
    };

    return (
        <ul>
            {todos.map(todo => (
                <li
                    key={todo.id}
                    style={{
                        textDecoration: todo.completed ? "line-through" : "none"
                    }}
                    onClick={() => toggleTodo(todo.id)}
                >
                    {todo.text}
                </li>
            ))}
        </ul>
    );
}

列表渲染时,每个列表项都需要一个唯一的key属性,帮助React识别哪些元素发生了变化。key应该是稳定且唯一的,通常使用数据的ID。


九、表单处理

9.1 受控组件

受控组件的值由React state控制,每个输入都有对应的状态更新逻辑。

import React, { useState } from "react";

function LoginForm() {
    const [formData, setFormData] = useState({
        username: "",
        password: "",
        remember: false
    });

    const handleChange = (e) => {
        const { name, value, type, checked } = e.target;
        setFormData(prev => ({
            ...prev,
            [name]: type === "checkbox" ? checked : value
        }));
    };

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log("表单数据:", formData);
    };

    return (
        <form onSubmit={handleSubmit}>
            <div>
                <label>用户名:</label>
                <input
                    type="text"
                    name="username"
                    value={formData.username}
                    onChange={handleChange}
                />
            </div>
            <div>
                <label>密码:</label>
                <input
                    type="password"
                    name="password"
                    value={formData.password}
                    onChange={handleChange}
                />
            </div>
            <div>
                <label>
                    <input
                        type="checkbox"
                        name="remember"
                        checked={formData.remember}
                        onChange={handleChange}
                    />
                    记住我
                </label>
            </div>
            <button type="submit">登录</button>
        </form>
    );
}

9.2 表单验证

function ValidatedForm() {
    const [formData, setFormData] = useState({ email: "" });
    const [errors, setErrors] = useState({});

    const validate = () => {
        const newErrors = {};

        if (!formData.email) {
            newErrors.email = "邮箱不能为空";
        } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
            newErrors.email = "邮箱格式不正确";
        }

        setErrors(newErrors);
        return Object.keys(newErrors).length === 0;
    };

    const handleSubmit = (e) => {
        e.preventDefault();
        if (validate()) {
            console.log("提交成功", formData);
        }
    };

    const handleChange = (e) => {
        setFormData({ [e.target.name]: e.target.value });
        // 清除错误
        if (errors[e.target.name]) {
            setErrors({ ...errors, [e.target.name]: "" });
        }
    };

    return (
        <form onSubmit={handleSubmit}>
            <div>
                <input
                    name="email"
                    value={formData.email}
                    onChange={handleChange}
                />
                {errors.email && <span className="error">{errors.email}</span>}
            </div>
            <button type="submit">提交</button>
        </form>
    );
}

十、组件通信

10.1 父子组件通信

// 父组件
function Parent() {
    const [message, setMessage] = useState("Hello from parent");

    return (
        <div>
            <h1>父组件</h1>
            <Child
                message={message}
                onMessageChange={setMessage}
            />
        </div>
    );
}

// 子组件
function Child({ message, onMessageChange }) {
    return (
        <div>
            <p>收到消息:{message}</p>
            <button onClick={() => onMessageChange("Hello from child")}>
                发消息给父组件
            </button>
        </div>
    );
}

10.2 Context

Context提供了一种在组件树中传递数据的方式,避免层层传递props。

import React, { createContext, useContext, useState } from "react";

// 创建Context
const ThemeContext = createContext();

function App() {
    const [theme, setTheme] = useState("light");

    return (
        <ThemeContext.Provider value={{ theme, setTheme }}>
            <div className={`app ${theme}`}>
                <Header />
                <Content />
            </div>
        </ThemeContext.Provider>
    );
}

function Header() {
    const { theme, setTheme } = useContext(ThemeContext);

    return (
        <header>
            <p>当前主题:{theme}</p>
            <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
                切换主题
            </button>
        </header>
    );
}

function Content() {
    const { theme } = useContext(ThemeContext);

    return (
        <main>
            <p>主题内容:{theme}</p>
        </main>
    );
}

10.3 组件通信方式对比

方式 适用场景
Props 父子组件直接通信
Context 跨多层组件共享数据
状态管理库 复杂应用全局状态管理
事件总线 非父子关系的组件通信

十一、React Router路由

11.1 基本使用

# 安装React Router
npm install react-router-dom
import React from "react";
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

function Home() {
    return <h1>首页</h1>;
}

function About() {
    return <h1>关于</h1>;
}

function User({ match }) {
    return <h1>用户ID: {match.params.id}</h1>;
}

function App() {
    return (
        <BrowserRouter>
            <nav>
                <Link to="/">首页</Link>
                <Link to="/about">关于</Link>
                <Link to="/user/123">用户</Link>
            </nav>

            <Routes>
                <Route path="/" element={<Home />} />
                <Route path="/about" element={<About />} />
                <Route path="/user/:id" element={<User />} />
            </Routes>
        </BrowserRouter>
    );
}

11.2 路由守卫

import { Navigate } from "react-router-dom";

function PrivateRoute({ isAuthenticated, children }) {
    return isAuthenticated ? children : <Navigate to="/login" />;
}

// 使用
<Route
    path="/dashboard"
    element={
        <PrivateRoute isAuthenticated={isLoggedIn}>
            <Dashboard />
        </PrivateRoute>
    }
/>

十二、状态管理

12.1 Redux基础

Redux是JavaScript应用的可预测状态容器,常用于React应用的状态管理。

# 安装Redux和React Redux
npm install @reduxjs/toolkit react-redux
import { configureStore, createSlice } from "@reduxjs/toolkit";

// 创建slice
const counterSlice = createSlice({
    name: "counter",
    initialState: { value: 0 },
    reducers: {
        increment: (state) => {
            state.value += 1;
        },
        decrement: (state) => {
            state.value -= 1;
        },
        incrementByAmount: (state, action) => {
            state.value += action.payload;
        }
    }
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;

// 创建store
const store = configureStore({
    reducer: {
        counter: counterSlice.reducer
    }
});

// Provider
import { Provider } from "react-redux";

function App() {
    return (
        <Provider store={store}>
            <Counter />
        </Provider>
    );
}

// 组件中使用
import { useSelector, useDispatch } from "react-redux";

function Counter() {
    const count = useSelector((state) => state.counter.value);
    const dispatch = useDispatch();

    return (
        <div>
            <p>计数:{count}</p>
            <button onClick={() => dispatch(increment())}>+</button>
            <button onClick={() => dispatch(decrement())}>-</button>
        </div>
    );
}

12.2 状态管理方案对比

方案 特点 适用场景
useState 简单直接 组件局部状态
useReducer 复杂逻辑 组件内复杂状态
Context 数据共享 主题、国际化等
Redux 强大生态 大型复杂应用
Zustand 轻量简单 中型应用
Jotai 原子化状态 灵活状态管理

十三、Hooks深入

13.1 useReducer

useReducer适用于复杂的状态逻辑,类似于Redux的单组件版本。

import React, { useReducer } from "react";

const initialState = { count: 0, status: "idle" };

function reducer(state, action) {
    switch (action.type) {
        case "increment":
            return { ...state, count: state.count + 1 };
        case "decrement":
            return { ...state, count: state.count - 1 };
        case "reset":
            return initialState;
        case "loading":
            return { ...state, status: "loading" };
        case "success":
            return { ...state, status: "success" };
        default:
            return state;
    }
}

function Counter() {
    const [state, dispatch] = useReducer(reducer, initialState);

    return (
        <div>
            <p>计数:{state.count}</p>
            <p>状态:{state.status}</p>
            <button onClick={() => dispatch({ type: "increment" })}>+</button>
            <button onClick={() => dispatch({ type: "decrement" })}>-</button>
            <button onClick={() => dispatch({ type: "reset" })}>重置</button>
        </div>
    );
}

13.2 useMemo与useCallback

import React, { useMemo, useCallback } from "react";

function ExpensiveComponent({ items, filter }) {
    // 缓存计算结果
    const filteredItems = useMemo(() => {
        console.log("计算过滤");
        return items.filter(item => item.name.includes(filter));
    }, [items, filter]);

    // 缓存函数
    const handleClick = useCallback((id) => {
        console.log("点击:", id);
    }, []);

    return (
        <ul>
            {filteredItems.map(item => (
                <li key={item.id} onClick={() => handleClick(item.id)}>
                    {item.name}
                </li>
            ))}
        </ul>
    );
}

useMemo缓存计算结果,避免不必要的重新计算。useCallback缓存函数引用,防止子组件不必要的重新渲染。


十四、最佳实践

14.1 组件设计原则

原则 说明
单一职责 每个组件只负责一个功能
高内聚低耦合 组件内部紧密相关,组件间相互独立
可复用性 通过props配置实现不同表现
可测试性 组件逻辑清晰,易于单元测试

14.2 性能优化

// 1. 使用React.memo避免不必要的渲染
const MemoizedComponent = React.memo(function MyComponent({ data }) {
    return <div>{data.name}</div>;
});

// 2. 使用useMemo和useCallback
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => doSomething(a), [a]);

// 3. 代码分割
import { lazy, Suspense } from "react";

const LazyComponent = lazy(() => import("./HeavyComponent"));

function App() {
    return (
        <Suspense fallback={<Loading />}>
            <LazyComponent />
        </Suspense>
    );
}

// 4. 虚拟列表(处理大量数据)
import { FixedSizeList } from "react-window";

function BigList() {
    return (
        <FixedSizeList
            height={400}
            itemCount={10000}
            itemSize={50}
        >
            {({ index, style }) => (
                <div style={style}>Item {index}</div>
            )}
        </FixedSizeList>
    );
}

14.3 常见错误避免

错误 正确做法
直接修改state 使用setState方法
缺少依赖数组 添加正确的依赖
内联函数作为props 使用useCallback缓存
不使用key 使用稳定的唯一key

总结

本教程系统介绍了React开发的各个方面,从基础概念到高级应用。主要内容包括:React核心概念和特点、JSX语法和组件开发、Props和State管理、React Hooks的深入使用、事件处理和表单操作、组件通信方式、React Router路由配置、Redux状态管理,以及性能优化技巧。React采用组件化的开发模式,配合Hooks和虚拟DOM,能够构建高性能的现代Web应用。掌握这些知识后,您可以独立开发功能完整的React应用。