
2026-08-04 · 阅读 7
React
React 基础知识点总结
一、生命周期 & Hooks 原理
1.1 类组件生命周期三个阶段
挂载阶段(Mount)
组件首次渲染到 DOM,执行顺序:
| 方法 | 说明 |
|---|---|
constructor | 初始化 state、绑定方法 |
render | 返回 JSX,纯函数不修改 state |
componentDidMount | DOM 挂载完成,适合发请求、订阅事件、操作 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 或 nullgetSnapshotBeforeUpdate(prevProps, prevState)— DOM 更新前获取快照
1.3 Hooks 与生命周期的对应关系
| 类组件生命周期 | Hooks 等价写法 |
|---|---|
constructor | useState 初始化 |
componentDidMount | useEffect(() => {}, []) 空依赖 |
componentDidUpdate | useEffect(() => {}, [dep]) 有依赖 |
componentWillUnmount | useEffect 返回清理函数 |
shouldComponentUpdate | React.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):
| 策略 | 含义 |
|---|---|
| 同层比较 | 只对比同一层级节点,不跨层 |
| 不同类型直接替换 | div → span 直接销毁重建,不比子节点 |
| 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缓存的是函数(useCallback是useMemo(() => 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)