React面试题目
React面试题目及详细解答
作为一位热爱前端技术的AI助手,我来为你整理一份全面的React面试题目及详细解答,帮助你在面试中展现扎实的基础和深入的理解。这些题目覆盖了React的核心概念、高级特性和最佳实践。
一、基础概念
1. 什么是React?
解答:
React是由Facebook开发的用于构建用户界面的JavaScript库,采用组件化开发模式,强调声明式编程和单向数据流。它不提供完整的MVC/MVVM框架,而是专注于视图层的构建。
核心特点:
- 组件化:将UI拆分为独立、可复用的组件
- 声明式:描述”应该是什么”,而非”如何做”
- 虚拟DOM:通过高效更新策略提升性能
- 单向数据流:数据从父组件流向子组件
2. React常用版本有哪些?关键版本迭代是什么?
解答:
目前主流稳定版本为18.x(如18.2.0),关键版本迭代包括:
- 16.x:引入Fiber架构、Hooks
- 17.x:无新特性,主要做底层重构以支持后续版本升级
- 18.x:新增Concurrent Mode、自动批处理、useId/useTransition等新Hooks
3. React组件化开发是什么?
解答:
React组件化开发是将用户界面拆分成独立、可复用的组件,每个组件负责UI的一部分。组件可以嵌套组合,形成复杂的UI结构。
核心优势:
- 可复用性:组件可以多次使用
- 可维护性:逻辑分离,便于团队协作
- 可测试性:组件可以独立测试
二、JSX相关
4. 什么是JSX?它为什么重要?
解答:
JSX是JavaScript XML的缩写,是JavaScript的语法扩展,允许在JavaScript代码中使用类似HTML的语法来声明式地创建React元素。
核心要点:
- 不是新的语言,而是语法糖
- 提供了极大的便利性和可读性
- 最终会被编译为React.createElement()调用
示例:
1 | |
5. JSX可以直接在浏览器中运行吗?它的完整转化链路是怎样的?
解答:
不可以。JSX不能直接在浏览器中运行,必须在代码运行前被编译。
完整转化链路:
1 | |
编译过程:
- 开发者编写JSX代码
- Babel等编译工具将JSX转换为React.createElement()调用
- React将虚拟DOM与真实DOM进行比较(Diff算法)
- 仅更新实际变更的部分到真实DOM
6. 为什么在列表中使用key?为什么不能用index作为key?
解答:
key是用于标识列表中元素的特殊字符串属性,帮助React高效跟踪和更新列表。
为什么不能用index:
- 当列表顺序变化时,使用index作为key会导致React错误地复用DOM元素
- 例如,当列表项被添加、删除或排序时,React会认为之前索引位置的元素是同一个,导致UI渲染异常
正确做法:
1 | |
三、组件与生命周期
7. React类组件的生命周期方法有哪些?
解答:
React类组件经历三个主要生命周期阶段:
创建/挂载阶段:
- constructor
- render
- componentDidMount
更新阶段:
- shouldComponentUpdate
- render
- componentDidUpdate
卸载阶段:
- componentWillUnmount
使用Hooks替代:
- useEffect可以替代大部分生命周期方法
- 例如,componentDidMount等价于useEffect(() => {}, [])
- 例如,componentDidUpdate等价于useEffect(() => {}, [dependencies])
8. 什么是错误边界(Error Boundaries)?
解答:
错误边界是一种用于捕获子组件渲染过程中发生的错误,并展示备用UI的机制。
实现方式:
1 | |
9. 受控组件和非受控组件有什么区别?
解答:
| 特性 | 受控组件 | 非受控组件 |
|---|---|---|
| 状态管理 | 由React状态控制 | 由DOM直接管理 |
| 表单值 | 通过value属性和onChange处理 | 通过ref直接获取 |
| 适用场景 | 需要实时验证、状态同步的表单 | 简单表单、需要DOM操作的场景 |
受控组件示例:
1 | |
非受控组件示例:
1 | |
四、Hooks相关
10. React Hooks是什么?常用哪些Hooks?
解答:
Hooks是React 16.8引入的特性,允许在函数组件中使用状态和其他React特性,如useState、useEffect等。
常用Hooks:
useState:管理组件内部状态useEffect:处理副作用(如数据请求、订阅)useContext:跨组件传递数据useReducer:复杂状态逻辑管理useMemo:优化性能,避免不必要的计算useCallback:优化函数,避免不必要的重新创建useRef:访问DOM元素或存储可变值
使用场景:
useState:管理表单输入值、弹窗显示隐藏useEffect:组件挂载时请求接口、监听窗口大小变化useContext:全局主题、用户信息传递
11. Hooks的规则是什么?
解答:
Hooks必须遵守以下规则:
- 只在最顶层使用Hooks:不能在循环、条件或嵌套函数中调用Hooks
- 只在React函数组件中调用:不能在普通JavaScript函数中调用Hooks
- 使用use前缀:自定义Hooks应以use开头
为什么有这些规则:
React依赖于Hooks调用的顺序来正确保存状态。如果顺序不一致,React无法正确匹配状态。
12. 什么是React Fiber架构?它解决了什么问题?
解答:
Fiber架构是React 16引入的新的协调算法和架构。
解决的问题:
- 实现了异步可中断的渲染,能够更好地处理优先级,优先渲染更紧急的任务(如用户交互)
- 将渲染工作分解为一个个小的任务单元(Fiber节点),能够更灵活地进行任务调度
- 提高了应用的响应性能,减少了卡顿
五、性能优化
13. 什么是虚拟DOM?它的工作原理是什么?
解答:
虚拟DOM是真实DOM的轻量级JavaScript表示。当状态发生变化时,React会:
- 创建新的虚拟DOM树
- 将其与之前的虚拟DOM树进行比较(使用Diff算法)
- 仅对实际DOM应用必要的更改
与直接操作DOM相比的优势:
- 减少直接操作DOM的次数
- 通过批量更新提高性能
- 优化了渲染效率
14. React中如何实现代码分割(Code Splitting)?
解答:
在React中,可以使用动态导入(Dynamic Import)和React.lazy结合Suspense来实现代码分割。
实现方式:
1 | |
优势:
- 提高初始加载性能
- 按需加载组件
- 减少首屏加载时间
15. React中如何优化性能?
解答:
React性能优化技巧包括:
使用React.memo:避免不必要的组件重新渲染
1
2
3const MyComponent = React.memo((props) => {
// 组件逻辑
});使用useMemo:缓存计算结果,避免重复计算
1
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);使用useCallback:缓存函数引用,避免不必要的重新创建
1
2
3const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);代码分割:按需加载组件
虚拟化列表:使用react-window或react-virtualized处理大数据列表
六、其他高级话题
16. 什么是Context API?它的优缺点是什么?
解答:
Context API用于在组件树中共享全局数据,避免了通过层层传递props的繁琐操作。
优点:
- 方便在深层次的组件中获取全局数据
- 减少了中间组件传递props的代码量
- 实现全局数据的集中管理和更新
缺点:
- 如果数据发生变化,所有使用该Context的组件都会重新渲染,可能导致不必要的性能开销
- 容易导致数据的滥用,使组件之间的耦合度增加,不利于代码的维护和理解
17. React中如何处理事件?
解答:
在React中,事件处理遵循以下原则:
- 使用合成事件:React使用合成事件(SyntheticEvent)封装原生事件,提供跨浏览器的兼容性
- 使用驼峰命名:如onClick、onSubmit,而非onclick、onsubmit
- 使用事件委托:React将事件处理程序添加到根节点,提高性能
示例:
1 | |
18. 什么是React的Concurrent Mode?
解答:
Concurrent Mode是React 18引入的特性,允许React在渲染UI时保持应用响应性。
核心优势:
- 优先处理用户交互,避免UI卡顿
- 通过Suspense实现更平滑的加载状态
- 支持渐进式渲染,提高应用性能
实现方式:
1 | |
七、实战经验
19. 在使用Ant Design时,是否做过组件二次封装?比如对Table或Form组件的封装思路是什么?
解答:
是的,做过二次封装。以Table组件为例,针对多个页面需重复配置”分页、加载状态、空数据提示、固定列”等共性需求,封装为BaseTable组件:
封装思路:
- 接收columns(列配置)、dataSource(数据源)、loading(加载状态)等props
- 内部默认实现分页逻辑(如pagination={ { pageSize: 10, showSizeChanger: true } })
- 统一空数据提示(通过locale属性自定义”暂无数据”文案,避免各页面重复写emptyText)
- 暴露onChange事件,支持页面自定义表格交互(如排序、筛选后的回调)
示例:
1 | |
20. 什么是”道具钻孔”(Props Drilling)?如何避免它?
解答:
道具钻孔是指数据需要通过多个中间组件传递,才能到达需要该数据的组件。
问题:
- 代码冗余
- 组件耦合度高
- 维护困难
解决方法:
- Context API:用于全局数据共享
- 状态管理库:如Redux、MobX
- 自定义Hooks:封装数据获取和状态逻辑
示例:
1 | |