虚拟 DOM
虚拟 DOM 是内存中的轻量级 DOM 树,状态变化时 React 先对比新旧虚拟 DOM 树(Diff 算法),只更新真实 DOM 中变化的部分,减少性能开销 。
什么是 JSX
JSX 是 JavaScript 的语法扩展,写法像 HTML,但实际会被编译成 React.createElement 调用,不能直接在浏览器运行,需要编译工具处理 。
类组件和函数组件有什么区别
类组件用 class 定义,有生命周期方法和 this 状态;函数组件用函数定义
useEffect
useEffect 替代了类组件的 componentDidMount、componentDidUpdate 和 componentWillUnmount,依赖数组控制执行时机,空数组只执行一次
合成事件和原生事件
React 事件是合成事件,统一绑定在 document 上,通过事件委托处理,兼容所有浏览器,阻止冒泡要用 stopPropagation 而非 return false。
HOC、Render Props 和 Hooks 怎么选
三者都解决代码复用,HOC 容易 props 冲突,Render Props 有嵌套地狱,Hooks 最简洁
如何优化 React 性能
用 React.memo 包裹组件避免不必要重渲染,用 useMemo 缓存计算结果,用 useCallback 缓存函数,大列表用虚拟滚动
受控组件和非受控组件
受控组件的表单值由 React 状态控制,非受控组件用 ref 直接读 DOM 值
React 18 的并发渲染是什么
并发渲染允许多个更新任务同时进行,优先级高的先渲染,用 createRoot 替代 ReactDOM.render,提升用户体验
Server Components 有什么用
服务端组件在服务器渲染,不发送 JS 到客户端,减少包体积,适合数据获取和静态内容
Redux 和 Context
小项目用 Context 够用,大项目状态复杂用 Redux 或 Zustand
Suspense 和 Lazy 怎么配合
Suspense 包裹 Lazy 加载的组件,加载时显示 fallback 内容,实现代码分割和按需加载
React 的核心特性有哪些
- 声明式编程:关注"做什么"而非"怎么做",只需描述UI状态,React 自动处理DOM更新;
- 组件化:UI拆分为独立可复用的组件,降低耦合、提升维护性;
- 单向数据流:数据从父组件流向子组件,子组件不直接修改父组件数据,保证数据可追溯;
- 虚拟DOM(VDOM) :用JS对象描述DOM结构,通过diff算法最小化DOM操作;
- JSX :语法糖(
React.createElement的简写),允许在JS中写HTML结构,提升开发效率
React 组件的两种定义方式及区别?
| 维度 | 函数组件(Function Component) | 类组件(Class Component) |
|---|---|---|
| 写法 | 纯函数,返回JSX | 继承React.Component,通过render返回JSX |
| 状态管理 | 需用Hooks(useState/useReducer) | 用this.state/this.setState |
| 生命周期 | 需用Hooks(useEffect) | 内置生命周期方法(componentDidMount等) |
| 性能 | 更轻量,无实例开销 | 有实例开销,略重 |
| TS支持 | 更简洁,类型推断友好 | 需绑定this,类型配置复杂 |
| 适用场景 | 大部分场景(React16.8+推荐) | 老项目/需复杂生命周期场景 |
React Hooks 出现的背景?解决了什么问题?
核心答案:
-
背景:类组件存在的痛点------
- 逻辑复用困难(HOC/Render Props 导致嵌套地狱);
- 复杂组件逻辑分散(生命周期方法中混杂不同逻辑,如componentDidMount同时请求数据+初始化定时器);
- this指向问题(类组件方法需绑定this,易出错);
-
解决的问题:
- 用Hooks将分散的逻辑聚合,按功能组织代码;
- 函数组件支持状态管理和生命周期逻辑;
- 逻辑复用更简洁(自定义Hooks),无嵌套地狱;
- 无需处理this指向问题。
常用 Hooks 及使用场景(useState/useEffect/useRef/useMemo/useCallback)
| Hook | 核心作用 | 典型使用场景 |
|---|---|---|
| useState | 函数组件声明状态 | 管理组件内部简单状态(如输入框值、弹窗显隐) |
| useEffect | 处理副作用(数据请求/定时器/事件绑定) | 组件挂载请求数据、卸载清理定时器/事件监听 |
| useRef | 保存可变值(不触发渲染) | 获取DOM元素、保存定时器ID、缓存上一次状态 |
| useMemo | 缓存计算结果(避免重复计算) | 复杂数据推导(如列表过滤排序)、减少不必要渲染 |
| useCallback | 缓存函数引用(避免函数重新创建) | 传递给子组件的回调函数(配合React.memo优化) |
useEffect 的依赖项及执行规则?常见坑?
核心执行规则:
- 依赖项为空数组
[]:仅在组件挂载时执行一次,卸载时执行返回的清理函数; - 依赖项为特定值
[a, b]:组件挂载时执行,且a/b变化时重新执行,同时先执行上一次的清理函数; - 无依赖项:组件每次渲染后都执行;
自定义 Hooks 的定义及使用场景?
核心答案:
- 定义:以
use开头的自定义函数,封装可复用的逻辑,内部可调用内置Hooks; - 核心规则:只能在函数组件/自定义Hooks中调用,不能在普通函数/条件语句中调用;
- 典型场景:请求数据、表单校验、防抖节流、监听窗口大小等。
自定义Hook示例(useRequest.js) :
js
// 封装通用请求逻辑
import { useState, useEffect } from 'react';
export function useRequest(api, params = []) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const fetchData = async () => {
try {
setLoading(true);
const res = await api(...params);
setData(res.data);
} catch (e) {
setError(e);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, params); // 参数变化重新请求
return { data, loading, error, refetch: fetchData };
}
// 组件中使用
import { useRequest } from '@/hooks/useRequest';
const { data, loading, refetch } = useRequest(api.getUserList, [1, 10]);
React.memo、useMemo、useCallback 的区别及使用场景?
| 优化手段 | 核心作用 | 适用场景 |
|---|---|---|
| React.memo | 缓存组件,仅props变化时重新渲染 | 纯展示型子组件,props不频繁变化 |
| useMemo | 缓存计算结果,避免重复计算 | 复杂计算(如列表过滤排序) |
| useCallback | 缓存函数引用,避免函数重新创建 | 传递给子组件的回调函数(配合React.memo) |
js
const Child = React.memo(({ onClick, data }) => {
return <button onClick={onClick}>{data}</button>;
});
// 父组件:用useCallback缓存函数,useMemo缓存数据
const Parent = () => {
const [count, setCount] = useState(0);
// 缓存函数,避免每次渲染重新创建
const handleClick = useCallback(() => {
console.log('click');
}, []);
// 缓存数据,避免每次渲染重新计算
const data = useMemo(() => `count: ${count}`, [count]);
return <Child onClick={handleClick} data={data} />;
};
React 虚拟DOM及diff算法原理?
虚拟DOM :用JS对象描述DOM节点的结构和属性(如 { type: 'div', props: { className: 'box' }, children: [] }),避免直接操作真实DOM(DOM操作比JS运算慢100+倍)。
diff算法核心规则(React 18) :
-
同层比较:只对比同一层级的节点,不跨层级比较(如根节点div不会和子节点p比较);
-
类型优先:节点类型不同(如div vs p),直接销毁旧节点、创建新节点;类型相同则对比props;
-
列表对比:
- 依赖key唯一标识节点,避免就地复用导致数据错乱;
- 采用"最长递增子序列"算法,最小化列表节点的移动次数;
-
React 18优化:支持并发渲染,diff过程可中断,优先处理高优先级任务(如用户输入)。
React 18 的核心新特性?
核心答案:
- 并发渲染(Concurrent Rendering) :渲染过程可中断、恢复,优先处理高优先级任务(如用户输入、动画),提升交互流畅度;
- 自动批处理(Automatic Batching) :将多个state更新合并为一次渲染,减少渲染次数(如Promise/定时器中的setState也会批处理);
- useTransition:标记低优先级更新(如列表筛选),避免阻塞高优先级操作(如输入框输入);
- Suspense:支持数据请求 Suspense(配合React Query/SWR),实现"加载中"占位,简化异步渲染逻辑;
- createRoot:替代ReactDOM.render,启用React 18新特性。
React 状态管理方案对比(useState/useReducer/Redux **/Zustand/Jotai)
| 方案 | 核心特点 | 适用场景 |
|---|---|---|
| useState | 轻量,管理组件内部简单状态 | 单个组件/小型组件状态 |
| useReducer | 基于Redux思想,管理复杂状态逻辑 | 组件内部复杂状态(如多状态联动) |
| Redux | 全局状态,中间件丰富(Thunk/Saga) | 大型应用,需严格状态规范 |
| Zustand | 轻量全局状态,无Provider嵌套 | 中小型应用,追求简洁 |
| Jotai | 原子化状态,细粒度更新 | 需精准控制渲染的场景 |
zustand实例代码
js
import { create } from 'zustand'
const useUserStore = create((set) => ({
name: '张三',
age: 20,
updateName: (newName) => set({ name: newName }),
incrementAge: () => set((state) => ({ age: state.age + 1 }))
}))
// 组件中使用
const User = () => {
const { name, age, updateName } = useUserStore()
return (
<div>
<p>{name}({age}岁)</p>
<button onClick={() => updateName('李四')}>修改姓名</button>
</div>
)
}
Lobster AIjsx
实现自定义 Hook useDebounce
js
import { useState, useEffect } from 'react';
// 防抖Hook:延迟执行函数,避免频繁触发
export function useDebounce(value, delay = 300) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
// 设置定时器,延迟更新
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// 每次value变化时,清除上一次的定时器
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// 组件中使用
const Search = () => {
const [input, setInput] = useState('');
// 防抖后的输入值
const debouncedInput = useDebounce(input, 500);
// 防抖后请求接口
useEffect(() => {
if (debouncedInput) {
api.search(debouncedInput).then((res) => {
setList(res.data);
});
}
}, [debouncedInput]);
return <input value={input} onChange={(e) => setInput(e.target.value)} />;
};
React Fiber ** 是什么?
- Fiber 是 React 的 协调引擎
- 将渲染任务拆分为 可中断的小单元
- 支持优先级调度
- Fiber 的诞生是为了解决 长时间渲染阻塞主线程 的问题。