react 面试题
基础
1 React 中的组件有哪几种,他们有什么区别?
React 组件主要分两类:类组件与函数组件
类组件通过class 关键字定义,需继承React.component,具备生命周期函数、this.state和this.setState等
推荐函数组件,更容易组合复用,性能优化更简单
2 什么是JSX 他与HTML 有什么不同?
JSX 是js 的语法扩展,允许在js 中书写类似html 的代码,用于描述ui 结构
jsx 本质是react.createElement 的语法糖,最终编译为js 对象
与html 不同的不同点包括 :所有标签必须闭合 /属性使用驼峰命名/表达式需要{} 包裹/可以嵌套任意js表达式
3 React 是如何实现虚拟DOM的?他的优势是什么?
react 中虚拟DOM 是js 对象描述真实dom 结构的中间表示。每次状态变化后,会生成新的虚拟DOM 树,并与旧的dom. 进行diff 运算,找出差异部分,再最小化更新真实dom
避免直接操作真实dom 提高性能
更好跨平台能力
有利于时间切片,异步渲染等高级特性
4 组件的生命周期有哪些?react 18中的变化是什么?
类组件生命周期分为三个阶段:
挂载阶段:constructor-render-componentDidMount
更新阶段:SCU(shouldComponentUpdate)-render-componentDidUpdate
卸载阶段:componentWillUnmount
React 18 之后建议使用函数组件 hook替代生命周期
5 React 中的key 的作用是什么?为什么不能用index?
key 在执行diff算法时更高效定位变动元素。如使用index,当顺序列表改变时可能会导致组件错误复用、状态混乱、动画异常。
只有在列表静态、增删改查时才可使用index
6 useEffect() 的依赖数组传入空数组 [] 时候,表示该副作用函数仅 在组件首次渲染时执行一次
请求初始化数据、设置事件监听、初始化定时器场景
7 受控组件和非受控组件的区别?分别适用于什么场景?
表单分为受控组件和非受控组件
受控组件:其值受react state控制,通过value 和onChange实现双向绑定
非受控组件:直接操作dom 读取表单,通常配合ref 使用
适用场景:受控组件适用于复杂交互和受状态驱动的场景
非受控组件适用于性能敏感或简单场景,如登录表单、文件上传等
8 react 中事件绑定有哪些方式?有什么坑需要注意?
jsx 中 绑定箭头函数
在组件外绑定函数 需要在constructor 中bind
hooks 专题
9 useEffect 有哪些使用场景?依赖组写错会带来什么问题?
useEffect 是处理副作用的hook
常见使用场景包括:
组件挂载时请求数据/监听事件或订阅操作/依赖变化时候执行/清理副作用(定时器/订阅)
若依赖组写错可能导致:
状态不一致 ,数据不同步,引发死循环或内存泄漏
借助eslint 自动检验
10 useReducer
useReducer ,用于管理和更新组件的状态。useState的替代方案。处理复杂方案更有优势
11 useRef 和useState的区别?分别适用于哪些场景?
useRef 和useState都可用于在函数组件中存储数据,但用途与行为不同
useState:用于声明响应式状态,状态变更会触发组件重新渲染
useRef:用于存储可变值,变更不会触发组件重新渲染,常用于保存dom 引用或渲染周期保存变量
场景对比:
表单输入值:useState
DOM 节点操作:useRef
记录上一次状态值或定时器id :useRef
控制重新渲染逻辑:useRef 可避免因频繁更新而导致的性能浪费
12 react 中如何避免useEffect 死循环?
导致死循环原因:
依赖组中包含每次render 都变化的值(如函数、对象、数组)
副作用函数更新了依赖项所关联的状态
解决方式包括:
使用useCallback 或useMemo 对函数和对象进行稳定处理
合理拆分多个useEffect ,细化每个副作用的关注点
使用空依赖数组 [] 表示只执行一次
严格遵循eslint 对依赖项的提示,必要时候注释忽略
13 为什么不能在循环?条件或者嵌套函数中调用hook?
只能在函数组件或自定义hook 的顶层调用hook
react 依赖调用顺序识别每个hook的位置
如在条件循环中调用hook 会导致调用顺序不一致 破坏内部hook 栈,从而引发运行时错误或者逻辑异常
14 常用的hooks
- useState:状态钩子 useState:用于定义组件的state ,类似于类定义的this.state的功能
- useReducer: 用于不管理复杂状态逻辑的替代方案,类似于redux的reducer
- useEffect:生命周期钩子
- useLayoutEffect: 在浏览器绘制完成之前同步
- useContext:获取context对象,用于在组件树中获取和使用共享的上下文
- useCallback:缓存回调函数,避免传入的回调每次都是新的函数实例而导致依赖数组重新渲染,具有性能优化效果
- useMemo:用于缓存传入的props ,避免依赖组件每次重新渲染
- useRef:获取组件的真实节点;用于在函数组件之间保存可变的值,并且不会引发重新渲染
- useImperativeHandle:用于自定义暴露给父组件的实例或方法
- useDebugValue:用于在开发者工具中显示自己的钩子相关标签
15 react hooks 平时开发中需要注意的问题?
只能在react 函数中使用
只能在最外层调用hook ,不能在if for 或者钩子函数中
useState 中存储的是引用数据类型,修改state得时候,一定要返回新的引用
性能优化与实战技巧
16 react 如何优化组件性能?常见手段有哪些?
react 性能优化的目标是减少无效渲染、避免重复计算与状态不一致
- 使用react.memo:包装函数组件,避免props无变化时重复渲染
- 使用useMemo:缓存复杂计算结果,仅依赖变化时重新计算
- 使用useCallback:缓存函数引用,防止子组件函数因变化而重新渲染
- 避免匿名函数和inline对象,避免每次render 创建新引用
- 拆分组件颗粒度:将大组件拆分为小组件,提升局部更新效率
- 懒加载 react.lazy+suspense :按需加载,减少初始包体积
- 虚拟滚动:使用react-virtualized等库提升长列表渲染性能
- 开启生产构建优化:取保production模式,开启tree-shaking和代码压缩
17 React.memo 、useMemo和useCallback的作用与区别?
useMemo 与useCallback 都用于性能优化,避免因重新渲染而导致的重新计算或函数引用变更
useMemo 返回计算结果,常用于缓存变量(如复杂计算、筛选、排序等)
useCallback 返回函数,常用语将函数传入子组件或者依赖中防止无限循环
使用场景:
useMemo :优化计算型表达式
useCallback:优化函数传参与事件处理器。
需注意过度使用
18 Context ? 使用时需要注意哪些事项?
跨组件层级共享数据的机制,适用于全局状态传递 如 主题、用户信息等。
const ThemeContext = React.createContext(defaultValue);
<ThemeContext.Provider value={…}>
</ThemeContext.Provider>
在子组件中使用 useContext(ThemeContext) 获取上下文值。
注意事项:
- 每次Provider 的value变化都会引发所有消费组件重新渲染
- 可配合useMemo 或分离Provider组件优化性能
- 不适用于频繁变化的局部状态(如输入框内容、临时弹窗)
19 react 中如何实现懒加载?如何配合suspense使用?
suspense 是React中用于处理“暂停”渲染的工具,允许你等待某些异步资源(比如动态加载组件或者数据)时,显示一个备用的ui(fallback)
核心作用:处理异步操作的中间状态,避免手动管理加载逻辑
<Suspense fallback={}>
与useTransition 配合使用,优化状态过渡和异步加载
使用场景:
- 动态懒加载:使用React.lazy 延迟加载组件,减少初始加载时间
- 获取数据:在组件渲染前等待数据加载(配合suspense的数据层)
- 逐步渲染:在复杂的应用中 分阶段显示ui
20 如何在react 中做错误边界处理?为什么函数组件不能做?
react 提供错误边界机制 处理组件数中的渲染错误(不包括事件,逻辑错误)
1 类组件 定义 componentDidCatch 和getDerviedStateFromError
2 将其报错目标组件 (错误边界只能捕获子组件错误)
21 react 18中有哪些新特性?
react 18 引入新特性,提升并发渲染与用户体验能力:
- 自动批处理 :多个state更新自动合并,减少render 次数
- 并发渲染架构:通过 startTransition 实现高优先级交互与低优先级更新并行处理;
- Transition API:可区分紧急与非紧急更新,提升响应性能;
- useId:解决 SSR 与客户端 ID 不一致的问题;
- ReactDOM.createRoot:替代 ReactDOM.render,支持并发模式;
- Streaming SSR with Suspense:服务器端渲染支持 Suspense 流式输出。
22 组件的通信方式
- 父传子:props 方式
- 子传父:父组件将自身函数传入,子组件调用该函数,父组件在函数中拿到子组件传递的数据
- 兄弟:找到共同的父节点,用父节点转发通信
- 跨层级:使用context可跨多层传递全局数据
- redux 维护一个全局状态中心 store
其他问题
23 react 中state和props 有什么区别?
props 是传递给组件的,而state 是组件内被组件自己管理的变量
props 是不可修改的,所有react 组件必须像纯函数一样保护props 不被更改,state是多变的可修改的,每次setState都异步更新
props只读,主要是为了维护组件的可预测性和可维护性
24 setState是同步的还是异步的
默认情况下是异步的,在react 生命周期都会走合并操作,延迟更新的策略。
react无法控制的地方:原生时间,具体addEventListener setTimeout setInterval 只能同步更新。
setState 的异步设计为了性能优化、减少渲染次数
25 React 有什么特点
声明式/组件化/虚拟DOM/单项数据流/生态系统 /JSX语法
优点:组件式开发,提高代码复用率。可以方便在客户端和服务端使用。JSX 代码可读性好。很容易与其它框架集成。比双向数据绑定更安全 ,速度更快
缺点:是一个库,不是一个完整的框架
26 类组件与函数组件有什么异同
- 语法不同、设计思想不通:函数式组件是函数式编程思想,而类组件式对象编程思想。面向对象编程将属性和方法封装起来,不利于测试。
- 生命周期不同、状态变量:类组件:somponentDidMount SCU 等生命周期钩子函数。函数组件:没有this 使用hooks 使用useState useEffect
- 复用性:类组件:使用hoc renderprops实现组件逻辑复用。函数式组件使用自定义hooks 。避免在循环/条件/判断/嵌套中调用hooks,保证调用顺序稳定。不能在useEffect 中使用useState
27 react 什么时候会触发重新渲染,重新渲染时发生了什么?
state 状态发生变化 / 父组件重新渲染引发子组件重新渲染 / props 改变
重新渲染时react 会生成新的虚拟dom 树,和旧的虚拟dom 使用diff 算法进行对比,将差异放入一个对象中,然后遍历对象,更新真实dom
28 何时使用refs
管理焦点,文本选择或者媒体播放。触发强制动画,集成第三方dom 库
29 react hooks 解决了哪些问题
- 组件状态逻辑复用困难:之前通过HOC 和render props 来实现。HOC 存在props 命名冲突,render props 会导致组件树嵌套过深
29 useEffect 与useLayoutEffect的区别
共同点:两者都是处理副作用的,这些副作用包括改变dom 、设置订阅、操作定时器等
区别:useEffect 是在组件完成渲染之后(包括首次渲染和更新渲染)异步触发的。他不会阻塞渲染过程。useLayoutEffect的副作用操作是在组件渲染完成布局阶段执行的 。这意味着他会在浏览器执行绘制之前执行,对dom 的计算和布局直接影响,因此useLayoutEffect的副作用操作会在浏览器更新屏幕之前同步触发。
如果在useLayoutEffect 时进行大量计算或阻塞操作,可会导致界面卡顿和不响应。因此,只有在dom 更新之前立即执行的某些操作才使用useLayoutEffect.
30 为什么useState需要使用数组而不是对象?
降低使用复杂度,使用数组可以直接根据顺序解构,而对象要是需要使用多次 就需要定义别名
31 对ssr 的理解
ssr 就是服务端渲染,他只得页面首次加载时,由服务端将数据和dom 组合成一个html字符串返回给前端,直接交给浏览器解析
优势:首屏加载速度会更快,有利于seo ,缺点:服务渲染会增大服务器压力,尤其是高并发访问的情况,会占用服务端cpu
32 对Portals 的理解
用于将子组件渲染到DOM 树的其他位置,而不受层次结构的限制
通常需要处理一些特殊的ui 布局,比如弹窗 可以放在body 上,不回受父组件容器的限制,仅仅是物理移动,依然可以读取父组件的上下文
createPortal(children,domNode,key)
33 父子组件的useEffect 哪个先执行?
先执行子组件 再执行父组件,react 保证了每次运行useEffect的同时,dom 都更新完毕。有一些场景如需要父组件在子组件前面执行,可以考虑一下useLayoutEffect