跳转到内容

React框架

💡

React 是前端三大框架之一,由 Meta(Facebook)开源。声明式编程、组件化架构、虚拟 DOM 是其三大核心特征。本文档系统覆盖 React 从入门到进阶的全部核心知识,适合学习和复习使用。


一、React 概述

1.1 React 是什么?

React 是一个用于构建用户界面的 JavaScript 库。它的核心思想是:

📌

声明式:你描述 UI 应该长什么样,React 负责高效更新 DOM。 组件化:将 UI 拆分为独立、可复用的组件,每个组件管理自己的状态。 一次学习,随处编写:React 可以渲染到 Web、移动端(React Native)、服务端(SSR)等多种平台。

虚拟 DOM 原理

React 在内存中维护一棵虚拟 DOM 树。状态变化时,React 通过 Diff 算法 对比新旧虚拟 DOM,计算出最小更新操作,再批量应用到真实 DOM。这避免了频繁操作真实 DOM 带来的性能问题。

状态变化 → 生成新虚拟DOM → Diff对比 → 计算最小变更 → 批量更新真实DOM

1.2 React vs Vue 全面对比

维度

React

Vue

开发者

Meta(Facebook)

Evan You + 社区

定位

UI 库(需搭配第三方方案)

渐进式框架(内置路由/状态管理)

编程范式

函数式编程为主

声明式 + 响应式

模板语法

JSX(JavaScript + XML)

SFC 单文件组件(template/script/style)

响应式原理

手动 setState / Hooks 触发更新

自动依赖追踪(Proxy / getter-setter)

组件通信

Props 向下 + Callback 向上 + Context

Props 向下 + Emit 向上 + Provide/Inject

状态管理

Redux / Zustand / Jotai

Pinia / Vuex

路由

React Router

Vue Router(官方维护)

TypeScript 支持

优秀(天然 JSX = TSX)

良好(3.0+ 全面支持)

学习曲线

较陡(JSX、Hooks 心智模型)

平缓(模板语法接近 HTML)

生态

极丰富,社区庞大

丰富,官方维护核心库

适用场景

大型应用、跨平台、生态灵活性要求高

中小型到大型应用、快速开发

选型建议:大型项目、需要跨平台(React Native)选 React;追求开发效率、渐进式迁移选 Vue。两者没有绝对优劣,取决于团队和场景。


二、环境搭建

2.1 使用 Vite 创建项目(推荐)

# 创建项目
npm create vite@latest my-react-app -- --template react

# 进入项目并安装依赖
cd my-react-app
npm install

# 启动开发服务器
npm run dev

2.2 项目目录结构

my-react-app/
├── index.html          # 入口 HTML
├── package.json        # 依赖与脚本
├── vite.config.js      # Vite 配置
├── src/
│   ├── main.jsx         # 应用入口(挂载根组件)
│   ├── App.jsx          # 根组件
│   ├── App.css          # 根组件样式
│   └── components/      # 组件目录
├── public/              # 静态资源

2.3 JSX 语法入门

JSX 是 JavaScript 的语法扩展,让你在 JS 中写类似 HTML 的代码。

// JSX 基本语法
const element = <h1>Hello, React!</h1>;

// 表达式用 {} 包裹
const name = '张三';
const greeting = <p>你好,{name}</p>;

// 条件渲染
const isLoggedIn = true;
const status = isLoggedIn ? <span>已登录</span> : <span>未登录</span>;

// 列表渲染
const items = ['Apple', 'Banana', 'Cherry'];
const list = (
  <ul>
    {items.map((item, index) => (
      <li key={index}>{item}</li>
    ))}
  </ul>
);

// 样式处理
const styleObj = { color: 'red', fontSize: '16px' };
const styled = <div style={styleObj}>内联样式</div>;
const classed = <div className="container">CSS 类名</div>;
💡

JSX 注意点:class → className、for → htmlFor、style 接受 JS 对象而非字符串、所有标签必须闭合(包括 &lt;br/&gt;&lt;img/&gt;)、return 只能返回一个根元素(可用 &lt;&gt;&lt;/&gt; Fragment 包裹)。


三、组件化开发

3.1 函数组件(推荐)

React 16.8 引入 Hooks 后,函数组件成为主流写法。

// 函数组件 — 接收 props,返回 JSX
function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

// 箭头函数写法
const Welcome = ({ name, age = 18 }) => {
  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>年龄: {age}</p>
    </div>
  );
};

// 使用组件
<Welcome name="张三" age={25} />

3.2 类组件(了解)

import React, { Component } from 'react';

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

函数组件 vs 类组件:函数组件更简洁、性能更好(无需实例化)、Hooks 让函数组件拥有类组件的全部能力。新项目一律推荐函数组件 + Hooks。

3.3 Props 传递与组件复用

// 父组件
function Parent() {
  const user = { name: '张三', role: 'admin' };

  return (
    <div>
      {/* Props 传递:原始值、对象、函数、JSX */}
      <Child
        name={user.name}
        user={user}
        onConfirm={() => console.log('确认')}
        footer={<button>提交</button>}
      />
    </div>
  );
}

// 子组件 — Props 是只读的,不可修改
function Child({ name, user, onConfirm, footer }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>角色: {user.role}</p>
      <button onClick={onConfirm}>确定</button>
      {footer}
    </div>
  );
}

3.4 事件处理

function EventDemo() {
  const handleClick = (e) => {
    e.preventDefault();      // 阻止默认行为
    e.stopPropagation();     // 阻止冒泡
    console.log('按钮被点击');
  };

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

  return (
    <div>
      <button onClick={handleClick}>点击我</button>
      <input onChange={handleChange} placeholder="输入内容" />
    </div>
  );
}

四、组件通信

4.1 父子通信(最常用)

// 父 → 子:通过 Props 传递数据
// 子 → 父:通过回调函数传递数据

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <Child
      count={count}                    // 父 → 子
      onIncrement={() => setCount(count + 1)}  // 子 → 父
    />
  );
}

function Child({ count, onIncrement }) {
  return (
    <div>
      <p>当前计数: {count}</p>
      <button onClick={onIncrement}>+1</button>
    </div>
  );
}

4.2 兄弟通信(状态提升)

// 将共享状态提升到最近的公共父组件

function Parent() {
  const [sharedData, setSharedData] = useState('');

  return (
    <div>
      <SiblingA onUpdate={setSharedData} />
      <SiblingB data={sharedData} />
    </div>
  );
}

4.3 跨层级通信(Context)

import { createContext, useContext, useState } from 'react';

// 1. 创建 Context
const ThemeContext = createContext('light');

// 2. 顶层提供数据
function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Page />
    </ThemeContext.Provider>
  );
}

// 3. 深层组件消费数据(无需通过中间组件传递)
function DeepChild() {
  const { theme, setTheme } = useContext(ThemeContext);

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

4.4 通信方式总结

场景

方案

适用情况

父→子

Props

直接父子关系

子→父

回调函数

直接父子关系

兄弟组件

状态提升

共享父组件较近

跨层级

Context / Redux / Zustand

深层嵌套或多组件共享

非关联组件

全局状态管理 / 事件总线

组件间无直接关系


五、React Hooks(核心)

5.1 useState — 状态管理

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);        // 基本类型
  const [user, setUser] = useState({             // 对象类型
    name: '张三',
    age: 25
  });

  const updateUser = () => {
    // 更新对象时需要使用展开运算符保留旧值
    setUser(prev => ({ ...prev, age: prev.age + 1 }));
  };

  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(c => c - 1)}>-1</button>
      <p>{user.name} - {user.age}岁</p>
      <button onClick={updateUser}>年龄+1</button>
    </div>
  );
}
💡

注意:setState 是异步的,多次调用会批处理。需要依赖前值请使用函数式更新 setCount(prev =&gt; prev + 1)

5.2 useEffect — 副作用处理

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    // 副作用:数据获取、订阅、DOM操作等
    const fetchData = async () => {
      try {
        const res = await fetch('https://api.example.com/data');
        const json = await res.json();
        setData(json);
      } finally {
        setLoading(false);
      }
    };

    fetchData();

    // 清理函数(组件卸载或依赖变化时执行)
    return () => {
      console.log('清理副作用');
    };
  }, []); // 空数组 = 仅在挂载时执行一次

  if (loading) return <div>加载中...</div>;
  return <div>{JSON.stringify(data)}</div>;
}

useEffect 依赖项说明

依赖项

执行时机

[] 空数组

仅在组件挂载时执行一次(componentDidMount)

[a, b] 指定依赖

挂载时 + a 或 b 变化时执行

不传第二个参数

每次渲染都执行

5.3 useRef — 引用与持久化

import { useRef, useEffect } from 'react';

function InputFocus() {
  const inputRef = useRef(null);       // DOM 元素引用
  const countRef = useRef(0);          // 持久化值(不触发重渲染)

  useEffect(() => {
    inputRef.current.focus();          // 自动聚焦输入框
  }, []);

  const handleClick = () => {
    countRef.current += 1;
    console.log('点击次数:', countRef.current);
  };

  return (
    <div>
      <input ref={inputRef} placeholder="自动聚焦" />
      <button onClick={handleClick}>点击计数(不触发渲染)</button>
    </div>
  );
}
📌

useRef vs useState:useRef 修改 .current 不会触发重渲染,适合存储 DOM 引用、定时器 ID、保存不需要响应式展示的值。

5.4 useMemo — 缓存计算结果

import { useMemo, useState } from 'react';

function ExpensiveList({ items, filter }) {
  // 仅在 items 或 filter 变化时才重新计算
  const filteredItems = useMemo(() => {
    console.log('重新过滤...');
    return items.filter(item => item.includes(filter));
  }, [items, filter]);

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

5.5 useCallback — 缓存回调函数

import { useCallback, useState } from 'react';

function Parent() {
  const [count, setCount] = useState(0);

  // 函数引用保持不变(除非 count 变化),避免子组件不必要的重渲染
  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []);

  return <Child onClick={handleClick} />;
}

// 配合 React.memo 使用效果最佳
const Child = React.memo(({ onClick }) => {
  console.log('Child 渲染');
  return <button onClick={onClick}>点击</button>;
});

5.6 useReducer — 复杂状态管理

import { useReducer } from 'react';

// Reducer 函数
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return { count: 0 };
    default:
      throw new Error(`Unknown action: ${action.type}`);
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </div>
  );
}
💡

useState vs useReducer:简单状态用 useState;多个相关状态或复杂的更新逻辑用 useReducer。useReducer 和 Redux 的模式类似,适合在小型场景替代 Redux。

5.7 自定义 Hook — 复用逻辑

// 自定义 Hook:封装可复用的逻辑
function useWindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });

  useEffect(() => {
    const handleResize = () => {
      setSize({ width: window.innerWidth, height: window.innerHeight });
    };

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return size;
}

// 使用
function App() {
  const { width, height } = useWindowSize();
  return <p>窗口大小: {width} x {height}</p>;
}

5.8 Hooks 使用规则

  1. 只在函数组件或自定义 Hook 中调用(不在普通 JS 函数、类组件中调用)
  2. 只在顶层调用(不在循环、条件、嵌套函数中调用)
  3. 自定义 Hook 必须以 use 开头命名

六、React Router 路由

6.1 安装与基本配置

npm install react-router-dom
// main.jsx — 根配置
import { BrowserRouter } from 'react-router-dom';

root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

6.2 路由定义(v6)

import { Routes, Route, Link, NavLink } from 'react-router-dom';

function App() {
  return (
    <div>
      <nav>
        {/* Link: 声明式导航 */}
        <Link to="/">首页</Link>
        {/* NavLink: 激活时自动添加 active 类名 */}
        <NavLink to="/about"
          className={({ isActive }) => isActive ? 'active' : ''}>
          关于
        </NavLink>
      </nav>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/user/:id" element={<User />} />        {/* 动态路由 */}
        <Route path="*" element={<NotFound />} />             {/* 404 */}
      </Routes>
    </div>
  );
}

6.3 动态路由与参数获取

import { useParams, useSearchParams } from 'react-router-dom';

function User() {
  const { id } = useParams();                    // 路径参数 /user/123
  const [searchParams] = useSearchParams();      // 查询参数 /user/123?tab=info

  return (
    <div>
      <p>用户 ID: {id}</p>
      <p>当前 Tab: {searchParams.get('tab')}</p>
    </div>
  );
}

6.4 编程式导航

import { useNavigate } from 'react-router-dom';

function LoginButton() {
  const navigate = useNavigate();

  const handleLogin = async () => {
    // 执行登录逻辑...
    const success = true;

    if (success) {
      navigate('/dashboard');              // 跳转
      // navigate('/dashboard', { replace: true });  // 替换(不可返回)
      // navigate(-1);                     // 返回上一页
    }
  };

  return <button onClick={handleLogin}>登录</button>;
}

6.5 嵌套路由

function App() {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        <Route index element={<Home />} />          {/* index = 默认子路由 */}
        <Route path="products" element={<Products />} />
        <Route path="settings" element={<Settings />} />
      </Route>
    </Routes>
  );
}

// Layout 组件中通过 <Outlet/> 渲染子路由
function Layout() {
  return (
    <div>
      <header>导航栏</header>
      <Outlet />   {/* 子路由内容在此渲染 */}
      <footer>底部</footer>
    </div>
  );
}

6.6 路由守卫

function ProtectedRoute({ children }) {
  const isLoggedIn = checkAuth();   // 检查登录状态

  if (!isLoggedIn) {
    return <Navigate to="/login" replace />;
  }

  return children;
}

// 使用
<Route path="/dashboard" element={
  <ProtectedRoute>
    <Dashboard />
  </ProtectedRoute>
} />

七、状态管理

7.1 Context API(轻量方案)

// 适合主题、语言、用户信息等全局但简单的状态
const AuthContext = createContext(null);

function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  const login = (userData) => setUser(userData);
  const logout = () => setUser(null);

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

7.2 Redux Toolkit(中大型应用推荐)

npm install @reduxjs/toolkit react-redux
// store/counterSlice.js
import { createSlice } from '@reduxjs/toolkit';

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;
export default counterSlice.reducer;

// store/index.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';

export const store = configureStore({
  reducer: {
    counter: counterReducer
  }
});

// 在组件中使用
import { useSelector, useDispatch } from 'react-redux';
import { increment } from './store/counterSlice';

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

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch(increment())}>+1</button>
    </div>
  );
}

7.3 Zustand(轻量替代)

npm install zustand
import { create } from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 })
}));

// 组件中使用
function Counter() {
  const count = useStore(state => state.count);
  const increment = useStore(state => state.increment);

  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
}

7.4 状态管理方案对比

方案

包大小

学习曲线

适用场景

Context + useReducer

0(内置)

小型应用、主题/语言等全局配置

Zustand

~1KB

中小型应用、快速开发

Redux Toolkit

~12KB

中大型应用、团队协作、需要 DevTools

Jotai / Recoil

~2KB

原子化状态管理、细粒度更新


八、性能优化

8.1 React.memo — 避免不必要的重渲染

const Child = React.memo(({ name }) => {
  console.log('仅在 name 变化时渲染');
  return <p>{name}</p>;
});

8.2 useMemo / useCallback

// useMemo: 缓存计算结果
const sortedList = useMemo(() => {
  return list.sort((a, b) => a.name.localeCompare(b.name));
}, [list]);

// useCallback: 缓存函数引用(配合 React.memo)
const handleClick = useCallback(() => {
  doSomething(id);
}, [id]);

8.3 懒加载(lazy + Suspense)

import { lazy, Suspense } from 'react';

// 组件按需加载,减少首屏 bundle 体积
const UserProfile = lazy(() => import('./UserProfile'));
const Settings = lazy(() => import('./Settings'));

function App() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <Routes>
        <Route path="/profile" element={<UserProfile />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  );
}

8.4 优化策略总结

策略

说明

React.memo

浅比较 Props,未变化时跳过渲染

useMemo

缓存计算开销大的结果

useCallback

稳定函数引用,配合 memo 使用

lazy + Suspense

代码分割,按需加载

key 的正确使用

避免无序或重复的 key

虚拟列表

长列表用 react-window / react-virtuoso


九、最佳实践

9.1 受控组件 vs 非受控组件

// 受控组件:表单数据由 React state 管理(推荐)
function ControlledForm() {
  const [value, setValue] = useState('');

  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}

// 非受控组件:表单数据由 DOM 自身管理(特殊场景)
function UncontrolledForm() {
  const inputRef = useRef(null);

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

  return <input ref={inputRef} defaultValue="" />;
}

9.2 错误边界(Error Boundary)

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <h1>出错了,请刷新页面重试。</h1>;
    }
    return this.props.children;
  }
}

// 包裹可能出错的组件
<ErrorBoundary>
  <RiskyComponent />
</ErrorBoundary>

9.3 常见反模式(应避免)

避免以下写法

  • 在 render 中直接修改 state(state.count++ 而非 setCount
  • 在 useEffect 中遗漏依赖项(或用 eslint-disable 注释掩盖问题)
  • 过度使用 useCallback / useMemo(只在真正需要性能优化时使用)
  • 使用 index 作为 key(列表会增删排序时)
  • 在条件语句 / 循环中调用 Hooks
  • 单个组件承担过多职责(超过 200 行考虑拆分)

9.4 组件设计原则

  1. 单一职责:每个组件只做一件事
  2. 可复用性:通过 Props 让组件适应不同场景
  3. 组合优于继承:React 推崇组合模式
  4. 状态最小化:状态放在需要它的最近的公共祖先
  5. Props 向下,事件向上:数据单向流动

学习路径建议:JSX 基础 → 函数组件 + Props → useState/useEffect → 条件/列表渲染 → 组件通信 → useRef/useMemo/useCallback → React Router → 状态管理(Context → Redux Toolkit) → 性能优化 → 项目实战。

掌握以上内容即可应对日常开发 90% 的场景。关键是多写代码,在实践中理解每个概念。

9.5 脚手架项目

npx create-react-app 项目名称
或者
npm create vite@latest 项目名称 -- --template react

以上两种方式都可以快速创建一个React项目。推荐使用Vite,更快更轻量。