Portfolio
← 返回博客列表
React

2026-08-04 · 阅读 7

React

React

React 基础知识点总结

一、生命周期 & Hooks 原理

1.1 类组件生命周期三个阶段

挂载阶段(Mount)

组件首次渲染到 DOM,执行顺序:

方法说明
constructor初始化 state、绑定方法
render返回 JSX,纯函数不修改 state
componentDidMountDOM 挂载完成,适合发请求、订阅事件、操作 DOM

更新阶段(Update)

props 或 state 变化时触发:

方法说明
shouldComponentUpdate返回 false 跳过更新,性能优化点
render重新渲染
componentDidUpdate更新完成,适合根据 props 变化做二次请求

卸载阶段(Unmount)

方法说明
componentWillUnmount清理定时器、取消订阅、取消请求
class UserList extends React.Component {
  state = { users: [], loading: true };

  componentDidMount() {
    fetch('/api/users').then(res => {
      this.setState({ users: res.data, loading: false });
    });
  }

  componentDidUpdate(prevProps) {
    if (prevProps.department !== this.props.department) {
      fetch(`/api/users?dept=${this.props.department}`);
    }
  }

  componentWillUnmount() {
    clearInterval(this.pollTimer);
  }

  render() {
    return this.state.loading
      ? '加载中...'
      : this.state.users.map(u => <div key={u.id}>{u.name}</div>);
  }
}

1.2 React 16.3 的生命周期变化

废弃(不安全):

  • componentWillMount → 用 componentDidMount 替代
  • componentWillReceiveProps → 用 getDerivedStateFromProps 替代
  • componentWillUpdate → 用 getSnapshotBeforeUpdate 替代

新增:

  • static getDerivedStateFromProps(props, state) — 静态方法,返回新 state 或 null
  • getSnapshotBeforeUpdate(prevProps, prevState) — DOM 更新前获取快照

1.3 Hooks 与生命周期的对应关系

类组件生命周期Hooks 等价写法
constructoruseState 初始化
componentDidMountuseEffect(() => {}, []) 空依赖
componentDidUpdateuseEffect(() => {}, [dep]) 有依赖
componentWillUnmountuseEffect 返回清理函数
shouldComponentUpdateReact.memo / useMemo / useCallback
function MyComponent({ userId }) {
  const [data, setData] = useState(null);

  // componentDidMount + componentDidUpdate
  useEffect(() => {
    fetchUser(userId).then(setData);
  }, [userId]);

  // componentWillUnmount
  useEffect(() => {
    const timer = setInterval(() => { /* ... */ }, 1000);
    return () => clearInterval(timer);
  }, []);

  return <div>{data}</div>;
}

1.4 useEffect 依赖数组与闭包陷阱

useEffect 的回调函数会形成闭包,捕获当次渲染时的变量值。

// ❌ 存在闭包陷阱,每次 count 变化都重建 interval
useEffect(() => {
  const timer = setInterval(() => {
    setCount(count + 1); // 闭包捕获的是创建时的 count
  }, 1000);
  return () => clearInterval(timer);
}, [count]);

// ✅ 函数式更新 + 空依赖,只创建一次 interval
useEffect(() => {
  const timer = setInterval(() => {
    setCount(prev => prev + 1); // 始终拿到最新 state
  }, 1000);
  return () => clearInterval(timer);
}, []);

二、虚拟 DOM & Diff 算法

2.1 什么是虚拟 DOM

虚拟 DOM 是真实 DOM 的 JavaScript 对象表示。React 通过对比新旧虚拟 DOM,找出最小变化量,只更新真实 DOM 中变化的部分。

// 真实 DOM
<div class="box"><h1>Hello</h1></div>

// 虚拟 DOM(JS 对象)
{
  type: 'div',
  props: { class: 'box' },
  children: [{ type: 'h1', props: {}, children: ['Hello'] }]
}

2.2 Diff 算法三大策略

React 通过三个假设将 Diff 复杂度从 O(n³) 降到 O(n):

策略含义
同层比较只对比同一层级节点,不跨层
不同类型直接替换divspan 直接销毁重建,不比子节点
Key 标识节点列表中通过 key 判断节点身份,复用已有节点

2.3 为什么不建议用 index 作为 key

// ❌ 用 index 做 key
list.map((item, index) => <li key={index}>{item.name}</li>)

// ✅ 用唯一 id 做 key
list.map(item => <li key={item.id}>{item.name}</li>)

原因: 在头部插入或删除元素时,所有后续元素的 index 都会变化,导致 React 误以为每个元素都变了,全部重新渲染。用唯一 id 可以确保 React 正确识别哪些节点可复用。

原列表:   [A, B, C]    key: 0, 1, 2
头部插入D: [D, A, B, C] key: 0, 1, 2, 3

用 index:React 认为 key=0 从 A→D,key=1 从 B→A,全部更新
用 id:   React 认为新增了 D,A/B/C 不变,只更新一项

2.4 Fiber 架构

React 16 引入 Fiber,将同步渲染改为可中断的异步渲染

  • 将渲染任务拆分成小单元(fiber 节点)
  • 支持暂停、恢复、丢弃不重要的更新
  • 利用浏览器空闲时间执行任务

三、状态管理 & 性能优化

3.1 状态管理方案对比

方案适用场景一句话
props/state父子组件传值直接通信,简单直接
Context跨层级传值,避免 prop drilling全局广播,所有消费者都会更新
Redux / RTK大型项目复杂状态逻辑统一管理,可预测的状态流
Zustand中小项目轻量替代简洁 API,学习成本低

Context 用法

const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <DeepChild />
    </ThemeContext.Provider>
  );
}

function DeepChild() {
  const theme = useContext(ThemeContext);
  return <div className={theme}>Hello</div>;
}

Context 缺点:Provider 值变化时,所有消费组件都会 re-render。

Redux 核心流程

View → dispatch(action) → Reducer(state, action) → new State → View 更新
  • Store:唯一数据源
  • Action:描述发生了什么,如 { type: 'ADD_TODO', payload: '学React' }
  • Reducer:纯函数,根据 action 计算新 state
  • dispatch:发出 action 的唯一方式

3.2 性能优化手段

手段解决问题原理
React.memo子组件不必要的渲染浅对比 props,没变跳过
useMemo重复的昂贵计算缓存计算结果,依赖不变不重算
useCallback函数引用变化导致子组件重渲染缓存函数,引用不变
虚拟列表大列表渲染卡顿只渲染可视区域元素
// React.memo:props 不变跳过渲染
const Child = React.memo(({ name }) => <div>{name}</div>);

// useMemo:缓存计算结果
const sorted = useMemo(() => list.sort(compareFn), [list]);

// useCallback:缓存函数引用
const handleClick = useCallback(() => {
  setCount(prev => prev + 1);
}, []);

useMemo 缓存的是useCallback 缓存的是函数useCallbackuseMemo(() => fn, deps) 的语法糖)。两者本身有开销,不应滥用,确认有性能问题时再使用。


四、TypeScript 在 React 中的应用

4.1 Props 类型

interface UserCardProps {
  name: string;
  age: number;
  avatar?: string;                      // 可选属性
  onClick: (id: number) => void;       // 函数类型
}

function UserCard({ name, age, avatar, onClick }: UserCardProps) {
  return (
    <div onClick={() => onClick(1)}>
      <img src={avatar || '/default.png'} />
      <p>{name} - {age}岁</p>
    </div>
  );
}

4.2 State 类型

interface Todo {
  id: number;
  text: string;
  done: boolean;
}

function TodoList() {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [loading, setLoading] = useState<boolean>(false);
}

4.3 事件处理类型

格式:React.事件类型<元素类型>

// 输入框变化
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  setValue(e.target.value);
};

// 点击事件
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
  e.preventDefault();
};

// 表单提交
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault();
};

// 键盘事件
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
  if (e.key === 'Enter') submit();
};

4.4 Ref 类型

// DOM 引用
const inputRef = useRef<HTMLInputElement>(null);

// 普通变量
const timerRef = useRef<number | null>(null);
const dataRef = useRef<UserData | undefined>();

4.5 泛型组件

interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
  keyExtractor: (item: T) => string;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return items.map(item => (
    <div key={keyExtractor(item)}>{renderItem(item)}</div>
  ));
}

五、高频面试题速查

Q1:类组件生命周期有哪些阶段?

挂载:constructor → render → componentDidMount 更新:shouldComponentUpdate → render → componentDidUpdate 卸载:componentWillUnmount React 16.3 后废弃了 componentWillMount / componentWillReceiveProps / componentWillUpdate

Q2:useEffect 闭包陷阱怎么解决?

使用 setCount(prev => prev + 1) 函数式更新 + 空依赖数组,避免闭包捕获旧值,也避免频繁重建。

Q3:Diff 算法策略?

同层比较、不同类型直接替换、Key 标识节点。复杂度 O(n³) → O(n)。不用 index 做 key,用唯一 id。

Q4:性能优化手段?

React.memo 防不必要渲染、useMemo 缓存值、useCallback 缓存函数、虚拟列表处理大列表。useMemo 缓存值,useCallback 缓存函数,不滥用。

Q5:React + TypeScript 类型定义?

Props → interface 定义;State → useState 泛型;事件 → React.事件类型<元素>;Ref → useRef<类型>(null)