2025.04.19react面试题

以下是整理的 20 道 React 面试题,涵盖基础、进阶和实战应用,适用于社招或内推准备:


一、React 基础(适合初中级)

  1. React 中的组件有哪几种?它们有什么区别?

  2. 什么是 JSX?它与 HTML 有什么不同?

  3. React 是如何实现虚拟 DOM 的?它的优势是什么?

  4. 组件的生命周期函数有哪些?React 18 中的变化是?

  5. React 中 key 的作用是什么?为什么不能用 index 作为 key?

  6. 如何在函数组件中模拟 componentDidMount?

  7. 受控组件和非受控组件的区别?分别适用于什么场景?

  8. React 中事件绑定有哪些方式?有什么坑需要注意?

  9. 如何实现组件之间的通信?

  10. React 如何实现条件渲染和列表渲染?


二、Hooks 专题(React 16.8+)

  1. useEffect 有哪些使用场景?依赖数组写错会带来什么问题?

  2. useRef 和 useState 的区别?分别适用于哪些场景?

  3. React 中如何避免 useEffect 死循环?

  4. 如何实现一个自定义 Hook?能举个例子吗?

  5. 为什么不能在循环、条件或嵌套函数中调用 Hook?


三、性能优化与实战技巧

  1. React 中如何避免不必要的重新渲染?有哪些优化手段?

  2. React.memo、useMemo 和 useCallback 的作用与区别?

  3. React 中如何实现懒加载?如何配合 Suspense 使用?

  4. 如何在 React 中做错误边界处理?为什么函数组件不能做?

  5. React18 中 Concurrent Mode 是什么?对开发者有什么影响?

好的,下面是前 5 道 React 面试题,以八股文风格整理,方便记忆和背诵:


  1. React 中的组件有哪几种?它们有什么区别?

答:
React 组件主要分为两类:类组件(Class Component)与函数组件(Function Component)。
类组件通过 class 关键字定义,需继承 React.Component,具备生命周期函数、this.state 和 this.setState 等特性;
函数组件最初为纯展示组件,自 React 16.8 引入 Hook 后也能拥有状态与副作用逻辑,写法更简洁。
当前推荐使用函数组件,因其更易组合复用,性能优化更简单,官方也逐步弃用类组件。


  1. 什么是 JSX?它与 HTML 有什么不同?

答:
JSX 是 JavaScript 的语法扩展,允许在 JavaScript 中书写类似 HTML 的代码,用于描述 UI 结构。
JSX 本质是 React.createElement 的语法糖,最终会被编译为 JavaScript 对象。
与 HTML 的不同点包括:

所有标签必须闭合;

属性使用驼峰命名,如 className、onClick;

表达式需用 {} 包裹;

可以嵌套任意 JS 表达式。


  1. React 是如何实现虚拟 DOM 的?它的优势是什么?

答:
React 中的虚拟 DOM 是用 JavaScript 对象描述真实 DOM 结构的中间表示。每次状态变化后,会生成新的虚拟 DOM 树,并与旧的树进行 Diff 运算,找出差异部分,再最小化更新真实 DOM。
其优势包括:

避免直接操作真实 DOM,提高性能;

更好的跨平台能力;

有利于实现时间切片、异步渲染等高级特性。


  1. 组件的生命周期函数有哪些?React 18 中的变化是?

答:
类组件的生命周期分为三个阶段:

挂载阶段:constructor → render → componentDidMount;

更新阶段:shouldComponentUpdate → render → componentDidUpdate;

卸载阶段:componentWillUnmount。
React 16 后废弃部分不安全生命周期,如 componentWillMount,推荐使用 getDerivedStateFromProps 和 getSnapshotBeforeUpdate 替代。
React 18 推出了自动批处理与并发特性,对生命周期感知更强,建议使用函数组件 + Hook 替代类组件管理生命周期。


  1. React 中 key 的作用是什么?为什么不能用 index?

答:
key 是 React 用于识别列表中元素的唯一标识,在执行 Diff 算法时能更高效定位变动元素。
若使用 index,当列表顺序改变时可能导致组件错误复用、状态错乱、动画异常等问题。
应优先使用唯一且稳定的业务 id 作为 key,只有在列表静态、不会增删改时才可使用 index。


好的,继续为你整理 第 6~10 题 的 React 八股文风格问答:


  1. 如何在函数组件中模拟 componentDidMount?

答:
函数组件中可使用 useEffect 模拟类组件的 componentDidMount。
写法如下:

useEffect(() => {
// 初始化逻辑
}, []);

当 useEffect 的依赖数组传入空数组 [] 时,表示该副作用函数仅在组件首次渲染时执行一次,行为等价于类组件中的 componentDidMount。
该模式常用于请求初始化数据、设置事件监听、初始化定时器等场景。


  1. 受控组件和非受控组件的区别?分别适用于什么场景?

答:
React 中,表单组件可分为受控组件和非受控组件。

受控组件:其值受 React state 控制,通过 value 和 onChange 实现双向绑定。
优点是数据可控,易于校验和联动;

非受控组件:直接操作 DOM 读取表单值,通常配合 ref 使用。

适用场景:

受控组件适用于复杂交互和受状态驱动的场景;

非受控组件适用于性能敏感或简单场景,如登录表单、文件上传等。


  1. React 中事件绑定有哪些方式?有什么坑需要注意?

答:
React 中常见的事件绑定方式包括:

在 JSX 中绑定箭头函数(推荐):
onClick={() => handleClick(id)}

在组件外定义绑定函数:
onClick={handleClick},注意需在 constructor 中 bind:
this.handleClick = this.handleClick.bind(this);

使用 class fields 语法(推荐):
handleClick = () => { … }

注意事项:

每次 render 重新创建箭头函数会影响性能;

函数组件中事件默认具有自动绑定能力,不用显式 bind;

React 事件是合成事件,非原生事件,具备跨浏览器兼容性,支持事件池优化(v17 之后逐步废弃事件池机制)。


好的,以下是 第 11~15 题(Hooks 专题) 的八股文风格标准问答:


  1. useEffect 有哪些使用场景?依赖数组写错会带来什么问题?

答:
useEffect 是 React 中用于处理副作用的 Hook,常见使用场景包括:

组件挂载时请求数据;

监听事件或订阅操作;

依赖变化时执行操作;

清理副作用(如定时器、订阅)。

其语法如下:

useEffect(() => {
// 副作用逻辑
return () => {
// 清理逻辑
};
}, [dependencies]);

若依赖数组写错(漏依赖、写错变量等),可能导致:

副作用函数未正确触发或频繁执行;

状态不一致、数据不同步;

引发死循环或内存泄漏。

应借助 ESLint 插件(如 eslint-plugin-react-hooks)自动校验依赖项完整性。


  1. useRef 和 useState 的区别?分别适用于哪些场景?

答:
useRef 和 useState 都可用于在函数组件中存储数据,但用途与行为不同:

useState:用于声明响应式状态,状态变更会触发组件重新渲染;

useRef:用于存储可变值,变更不会触发组件重新渲染,常用于保存 DOM 引用或跨渲染周期保存变量。

适用场景对比如下:

表单输入值:useState;

DOM 节点操作:useRef;

记录上一次状态值或定时器 ID:useRef;

控制重新渲染逻辑:useRef 可避免因频繁更新而导致的性能浪费。


  1. React 中如何避免 useEffect 死循环?

答:
useEffect 死循环通常由以下原因导致:

依赖数组中包含每次 render 都变化的值(如函数、对象、数组);

副作用函数中更新了依赖项所关联的状态。

解决方式包括:

使用 useCallback 或 useMemo 对函数和对象进行稳定处理;

合理拆分多个 useEffect,细化每个副作用的关注点;

使用空依赖数组 [] 表示只执行一次(等价于 componentDidMount);

严格遵循 ESLint 对依赖项的提示,必要时用注释忽略。


  1. 如何实现一个自定义 Hook?能举个例子吗?

答:
自定义 Hook 是以 use 开头的函数,用于复用逻辑片段,让组件更简洁、更易维护。

示例:封装一个监听窗口大小的 Hook

import { useState, useEffect } from ‘react’;

function useWindowSize() {
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });

useEffect(() => {
const handleResize = () => {
setSize({ width: window.innerWidth, height: window.innerHeight });
};
window.addEventListener(‘resize’, handleResize);
return () => window.removeEventListener(‘resize’, handleResize);
}, []);

return size;
}

使用方式:

const { width, height } = useWindowSize();

该 Hook 封装了监听逻辑,提升代码复用性和可读性。


  1. 为什么不能在循环、条件或嵌套函数中调用 Hook?

答:
React 的 Hook 调用必须遵循 “Hook 规则”,即:

只能在函数组件或自定义 Hook 的顶层调用 Hook;

不能在循环、条件、嵌套函数中调用 Hook。

原因是 React 依赖调用顺序识别每个 Hook 的位置。
若在条件或循环中调用 Hook,会导致调用顺序不一致,破坏内部 Hook 栈,从而引发运行时错误或逻辑异常。

官方提供 ESLint 插件 eslint-plugin-react-hooks,可帮助开发者自动检测 Hook 使用规范。


好的,下面是 第 16~20 题(性能优化与实战技巧) 的 React 八股文风格标准问答:


  1. React 如何优化组件性能?有哪些常见手段?

答:
React 性能优化的目标是减少无效渲染、避免重复计算与状态不一致。常见优化手段包括:

  1. 使用 React.memo:包装函数组件,避免 props 无变化时重复渲染;

  2. 使用 useMemo:缓存复杂计算结果,仅在依赖变化时重新计算;

  3. 使用 useCallback:缓存函数引用,防止子组件因函数变化而重渲染;

  4. 避免匿名函数和 inline 对象:避免每次 render 创建新引用;

  5. 拆分组件粒度:将大组件拆分为小组件,提升局部更新效率;

  6. 懒加载(React.lazy + Suspense):按需加载组件,减少初始包体积;

  7. 虚拟滚动:使用 react-window、react-virtualized 等库提升长列表渲染性能;

  8. 开启生产构建优化:确保使用 production 模式打包,开启 tree-shaking 和代码压缩。


  1. useMemo 和 useCallback 有什么区别?使用场景分别是什么?

答:
useMemo 与 useCallback 都用于性能优化,避免因重新渲染导致的重复计算或函数引用变更。

useMemo(fn, deps):返回 计算结果,常用于缓存变量(如复杂计算、筛选、排序等);

useCallback(fn, deps):返回 函数引用,常用于将函数传入子组件或用于依赖中防止无限循环。

使用场景:

useMemo:优化计算型表达式;

useCallback:优化函数传参与事件处理器。
需注意过度使用反而适得其反,建议仅在明显性能瓶颈场景下使用。


  1. 什么是 Context?使用时有哪些注意事项?

答:
React Context 是用于跨组件层级共享数据的机制,适用于全局状态传递如主题、用户信息等。核心 API 包括:

const ThemeContext = React.createContext(defaultValue);


在子组件中使用 useContext(ThemeContext) 获取上下文值。

注意事项:

每次 Provider 的 value 变化都会引发所有消费组件重新渲染;

可配合 useMemo 或分离 Provider 组件优化性能;

不适用于频繁变化的局部状态(如输入框内容、临时弹窗);


  1. React 中如何处理错误?如何实现错误边界?

答:
React 提供错误边界机制处理组件树中的渲染错误(不包括事件、异步逻辑等)。

实现方法:

  1. 创建类组件,定义 componentDidCatch 和 getDerivedStateFromError:

class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
// 上报错误日志
}
render() {
return this.state.hasError ? : this.props.children;
}
}

  1. 使用方式:将其包裹目标组件

注意:错误边界只能捕获其子组件中的错误。


  1. React18 中的新特性有哪些?和之前版本有何不同?

答:
React 18 引入了多项新特性,提升并发渲染与用户体验能力:

  1. 自动批处理(Automatic Batching):多个 state 更新自动合并,减少 render 次数;

  2. 并发渲染架构(Concurrent Features):通过 startTransition 实现高优先级交互与低优先级更新并行处理;

  3. Transition API:可区分紧急与非紧急更新,提升响应性能;

const [isPending, startTransition] = useTransition();
startTransition(() => {
setSearchQuery(input);
});

  1. useId:解决 SSR 与客户端 ID 不一致的问题;

  2. ReactDOM.createRoot:替代 ReactDOM.render,支持并发模式;

  3. Streaming SSR with Suspense:服务器端渲染支持 Suspense 流式输出。

React 18 标志着进入“并发时代”,推荐逐步迁移核心项目使用新版能力。


你可能感兴趣的:(前端)