React

160 阅读8分钟

React

1. React 是什么?核心特点

React 是 Meta 开源的 JavaScript UI 库,专注于构建用户界面。核心特点

  • 声明式编程:描述 UI 状态,React 自动处理 DOM 更新。
  • 组件化:UI 拆分为独立可复用的组件。
  • 单向数据流:数据从父组件流向子组件,可预测、易调试。
  • 虚拟 DOM + Fiber:高效更新,可中断渲染。
  • JSX:JavaScript 语法扩展,允许在 JS 中写类似 HTML 的标记。

2. JSX

JSX 是 React.createElement 的语法糖

// JSX 写法
const element = <h1 className="title">Hello React</h1>;

// 编译后
const element = React.createElement('h1', { className: 'title' }, 'Hello React');

浏览器无法识别 JSX,需要通过 Babel 编译为普通 JS 代码才能执行

3、组件间通信

React 组件间通信方式取决于组件关系,主要方式如下

方式适用场景示例
Props父→子传递数据<Child message={msg} />
回调函数子→父传递数据父传函数给子,子调用 props.onChildData(data)
Context API跨层级组件通信(避免 props 逐层传递)createContext + useContext
状态管理库大型应用全局状态Redux Toolkit、Zustand、Jotai
Refs直接访问 DOM 或子组件实例useRef + ref 属性
自定义 Hooks复用状态逻辑useAuthuseFetch 等
高阶组件(HOC)共享逻辑(较少使用,Hooks 更优)接收组件返回增强组件
Event Bus(事件总线)任意组件通信(非 React 原生)mitt 等第三方库

createContext、useContext 示例:

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

// 1. 创建 Context 对象
const ThemeContext = createContext('light'); // 参数是默认值

// 2. Provider 组件:提供数据
function App() {
  const [theme, setTheme] = useState('dark');
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

// 3. 后代组件消费数据
function Toolbar() {
  return <ThemedButton />;
}

function ThemedButton() {
  const { theme, setTheme } = useContext(ThemeContext); // 方式一:useContext
  return (
    <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
      当前主题:{theme}
    </button>
  );
}

4、API

4.1 内置 Hooks

Hook说明
useState在函数组件中添加和管理局部状态,返回当前状态和更新函数
useReducer用于管理包含多个子值或依赖先前状态的复杂组件逻辑,基于 reducer 模式
useContext读取并订阅组件中的 Context 值,避免 props 逐层传递
useRef声明一个可变引用,可以保存任何可变值,最常见的用途是访问 DOM 元素
useImperativeHandle自定义通过 ref 暴露给父组件的实例值,通常与 forwardRef 配合使用
useEffect将组件连接到外部系统并处理副作用,如数据获取、订阅、手动 DOM 操作,在渲染后执行
useLayoutEffect在浏览器重新绘制屏幕之前同步触发,用法与 useEffect 相同,但会阻塞视觉更新
useInsertionEffect在 DOM 变异之前触发,专为 CSS-in-JS 库注入样式而设计
useMemo缓存昂贵计算的结果,避免在每次渲染时重复计算,仅在依赖项变化时重新计算
useCallback缓存函数定义,防止因函数重新创建导致的子组件不必要重新渲染
useTransition将状态更新标记为"过渡",这种更新可以被中断,以避免阻塞用户界面
useDeferredValue延迟更新 UI 的某一部分,以优先响应用户输入
useId生成在客户端和服务器上保持稳定的唯一 ID,主要用于可访问性属性
useDebugValue在 React DevTools 中为自定义 Hook 添加标签,用于调试
useSyncExternalStore允许函数组件订阅外部 store(如第三方状态管理库或浏览器 API)
useOptimistic允许在后台操作完成前乐观地更新 UI,提供即时反馈
useActionState管理表单 action 的状态,包括 pending 状态和返回数据
use通用的资源读取 API,用于读取 Promise 或 Context 等资源的值,可以在条件语句中调用

4.2 内置组件

这些是可以在 JSX 中使用的 React 内置组件,以 Symbol 常量形式导出

组件说明
<Fragment>让你无需向 DOM 添加额外节点即可对子元素列表进行分组,支持简写 <>...</>
<Profiler>用于编程式测量 React 应用的渲染性能
<StrictMode>用于检测应用中潜在问题的工具,不会渲染任何可见 UI
<Suspense>允许在子组件完成加载前显示一个回退 UI
<Activity>React 19 新增 API,用于隐藏和恢复其子组件的 UI 和内部状态

4.3 工具类 API

API说明
createContext创建一个 Context 对象,可供组件向其子组件提供数据,搭配 useContext 使用
forwardRef允许组件将 DOM 节点作为 ref 暴露给父组件,搭配 useRef 使用
lazy允许在组件第一次被渲染前延迟加载其代码,实现代码分割
memo允许组件在 props 未发生变化时跳过重新渲染,搭配 useMemo 和 useCallback 使用
startTransition允许将状态更新标记为非紧急的,与 useTransition 类似但用于非 Hook 场景
act在测试中用于包裹渲染和交互,确保在断言前所有更新已处理完毕
createElement创建 React 元素,通常用 JSX 替代,但可在非 JSX 环境中使用
cloneElement克隆并返回一个新的 React 元素,可覆盖原元素的 props
isValidElement检查某个值是否为 React 元素
Children提供 mapforEachcountonlytoArray 等工具方法,用于处理 props.children 不透明数据结构
Component定义类组件的基类
PureComponent与 Component 类似,但自带 shouldComponentUpdate 浅比较实现
createRef创建 ref 对象,类组件中用于访问 DOM 元素

4.4 通用 DOM API

API说明
createPortal允许将子组件渲染到 DOM 树中父组件 DOM 层次之外的不同位置,常用于模态框、全局提示等
flushSync强制 React 同步执行状态更新并立即刷新 DOM

4.5 资源预加载 API

这些 API 用于预加载脚本、样式表、字体等资源,从而让应用更快。基于 React 的框架通常会自动处理资源加载

API说明
prefetchDNS预解析 DNS 域名,提前获取 IP 地址,减少后续请求的 DNS 查询时间
preconnect提前连接到预计请求资源的服务器,建立 TCP 连接和 TLS 握手,即使尚不确定具体需要哪些资源
preload预获取并缓存预计要使用的资源(如样式表、字体、图片、外部脚本),但不执行,可节省时间
preloadModule预获取预计要使用的 ESM 模块,但不执行
preinit预获取并执行外部脚本,或预获取并插入样式表
preinitModule预获取并执行一个 ESM 模块

4.6 通用 DOM API

API说明
createPortal允许将子组件渲染到 DOM 树中父组件 DOM 层次之外的不同位置,常用于模态框、全局提示等
flushSync强制 React 同步执行状态更新并立即刷新 DOM

5、React Router(路由)

React Router v6 完全利用 Hooks 重构。

核心组件

组件作用
BrowserRouterhistory 模式路由容器
HashRouterhash 模式路由容器
Routes / Route定义路由规则
Link / NavLink声明式导航
Outlet嵌套路由占位符

核心 Hooks

Hook作用
useParams获取路由参数
useLocation获取当前 location 对象
useNavigate程序化导航
useRoutes配置式路由(替代 Routes + Route

6、React-Redux

API说明
<Provider store>顶层组件,使 store 对下层组件可用。
connect(mapStateToProps, mapDispatchToProps, mergeProps, options)高阶组件(HOC),将 store 中的 state 和 dispatch 映射到组件的 props。
useSelector(selector)Hook,从 store 中提取数据,当数据变化时强制组件重新渲染。
useDispatch()Hook,返回 store 的 dispatch 函数。
useStore()Hook,返回 store 实例本身(不常用)。

Redux 现代推荐: (Redux Toolkit + React-Redux Hooks )

必须掌握的核心 API

API作用一句话说明
configureStore创建 store像 createStore 但更智能,自动加 thunk 和 DevTools
createSlice同时生成 reducer 和 action creators传入 name、initialState、reducers 对象,自动生成
createAsyncThunk处理异步 action自动生成 pending/fulfilled/rejected 三个 action,并在 extraReducers 中处理
// store.js
import { configureStore, createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// 1. 同步 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 }
  }
});

// 2. 异步 thunk
export const fetchUser = createAsyncThunk('user/fetch', async (userId) => {
  const res = await fetch(`/api/user/${userId}`);
  return res.json();
});

// 3. 异步 slice (处理 thunk 的三种状态)
const userSlice = createSlice({
  name: 'user',
  initialState: { data: null, loading: false, error: null },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => { state.loading = true })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.loading = false;
        state.data = action.payload;
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      });
  }
});

// 导出 action creators 和 reducer
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
const store = configureStore({
  reducer: {
    counter: counterSlice.reducer,
    user: userSlice.reducer
  }
});

export default store;

React-Redux Hooks —— 在组件里用状态

两个核心 Hook

Hook作用类比
useSelector从 store 中读取数据类似 mapStateToProps
useDispatch拿到 dispatch 函数类似 mapDispatchToProps

使用步骤(配合上面的 store)

1. 顶层用 Provider 包裹

// main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')).render(
  <Provider store={store}>
    <App />
  </Provider>
);

2. 组件内读取和派发

// Counter.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement, incrementByAmount } from './store';

function Counter() {
  // 读取状态
  const count = useSelector(state => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <span>{count}</span>
      <button onClick={() => dispatch(increment())}>+1</button>
      <button onClick={() => dispatch(decrement())}>-1</button>
      <button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
    </div>
  );
}

3. 异步 thunk 的派发

import { fetchUser } from './store';

function UserProfile({ userId }) {
  const dispatch = useDispatch();
  const { data, loading, error } = useSelector(state => state.user);

  useEffect(() => {
    dispatch(fetchUser(userId));
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  return <div>{data?.name}</div>;
}

7、React 底层原理

1. 虚拟 DOM

虚拟 DOM 是用 JS 对象描述真实 DOM 结构

好处

  • 减少频繁的真实 DOM 操作
  • 跨平台(React Native)
  • 便于 diff 算法计算差异

2. Diff 算法

核心策略

  • 同层比较,不跨层级
  • 类型不同 → 直接销毁重建
  • 类型相同 → 对比属性
  • 列表靠 key 识别节点复用,复杂度优化到 O(n)

为什么 key 不能用 index?
数组增删前置元素会导致 index 错乱,引发组件状态错位、DOM 复用错误。应使用唯一稳定的业务 id

3. Fiber 架构(React 16+)

解决旧 React 一次性渲染卡死主线程的问题

两个阶段

  • Render 阶段:可中断、分片、优先级调度,遍历构建 Fiber 树
  • Commit 阶段:不可中断,一次性更新 DOM、布局、绘制

4. React 更新流程

触发 setState → 生成更新任务 → Fiber 调和(可中断)→ 收集 DOM 变更 → Commit 一次性渲染 → 浏览器绘制

5. 合成事件

React 自己实现了一套事件系统(事件委托到 root 节点),性能高,与原生事件混用时,原生先执行 → 合成后执行,阻止冒泡互不生效