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
2
3
4
5
// JSX
const element = <h1>Hello, world</h1>;

// 编译后
const element = React.createElement('h1', null, 'Hello, world');

5. JSX可以直接在浏览器中运行吗?它的完整转化链路是怎样的?

解答
不可以。JSX不能直接在浏览器中运行,必须在代码运行前被编译。

完整转化链路

1
JSX -> React.createElement()调用 -> 虚拟DOM对象 -> 真实DOM元素

编译过程

  1. 开发者编写JSX代码
  2. Babel等编译工具将JSX转换为React.createElement()调用
  3. React将虚拟DOM与真实DOM进行比较(Diff算法)
  4. 仅更新实际变更的部分到真实DOM

6. 为什么在列表中使用key?为什么不能用index作为key?

解答
key是用于标识列表中元素的特殊字符串属性,帮助React高效跟踪和更新列表。

为什么不能用index

  • 当列表顺序变化时,使用index作为key会导致React错误地复用DOM元素
  • 例如,当列表项被添加、删除或排序时,React会认为之前索引位置的元素是同一个,导致UI渲染异常

正确做法

1
2
3
4
5
// 使用唯一标识符(如id)
{items.map(item => <li key={item.id}>{item.name}</li>)}

// 错误做法:使用index
{items.map((item, index) => <li key={index}>{item.name}</li>)}

三、组件与生命周期

7. React类组件的生命周期方法有哪些?

解答
React类组件经历三个主要生命周期阶段:

  1. 创建/挂载阶段

    • constructor
    • render
    • componentDidMount
  2. 更新阶段

    • shouldComponentUpdate
    • render
    • componentDidUpdate
  3. 卸载阶段

    • componentWillUnmount

使用Hooks替代

  • useEffect可以替代大部分生命周期方法
  • 例如,componentDidMount等价于useEffect(() => {}, [])
  • 例如,componentDidUpdate等价于useEffect(() => {}, [dependencies])

8. 什么是错误边界(Error Boundaries)?

解答
错误边界是一种用于捕获子组件渲染过程中发生的错误,并展示备用UI的机制。

实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

componentDidCatch(error, info) {
this.setState({ hasError: true });
}

render() {
if (this.state.hasError) {
return <div>Something went wrong.</div>;
}
return this.props.children;
}
}

// 使用
function MyApp() {
return (
<ErrorBoundary>
<MyComponentThatMightError />
</ErrorBoundary>
);
}

9. 受控组件和非受控组件有什么区别?

解答

特性 受控组件 非受控组件
状态管理 由React状态控制 由DOM直接管理
表单值 通过value属性和onChange处理 通过ref直接获取
适用场景 需要实时验证、状态同步的表单 简单表单、需要DOM操作的场景

受控组件示例

1
2
3
4
5
6
7
8
9
10
11
function ControlledComponent() {
const [inputValue, setInputValue] = useState('');

return (
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
);
}

非受控组件示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function UncontrolledComponent() {
const inputRef = useRef(null);

const handleSubmit = (event) => {
event.preventDefault();
console.log(inputRef.current.value);
};

return (
<form onSubmit={handleSubmit}>
<input type="text" ref={inputRef} />
<button type="submit">Submit</button>
</form>
);
}

四、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必须遵守以下规则:

  1. 只在最顶层使用Hooks:不能在循环、条件或嵌套函数中调用Hooks
  2. 只在React函数组件中调用:不能在普通JavaScript函数中调用Hooks
  3. 使用use前缀:自定义Hooks应以use开头

为什么有这些规则
React依赖于Hooks调用的顺序来正确保存状态。如果顺序不一致,React无法正确匹配状态。

12. 什么是React Fiber架构?它解决了什么问题?

解答
Fiber架构是React 16引入的新的协调算法和架构。

解决的问题

  • 实现了异步可中断的渲染,能够更好地处理优先级,优先渲染更紧急的任务(如用户交互)
  • 将渲染工作分解为一个个小的任务单元(Fiber节点),能够更灵活地进行任务调度
  • 提高了应用的响应性能,减少了卡顿

五、性能优化

13. 什么是虚拟DOM?它的工作原理是什么?

解答
虚拟DOM是真实DOM的轻量级JavaScript表示。当状态发生变化时,React会:

  1. 创建新的虚拟DOM树
  2. 将其与之前的虚拟DOM树进行比较(使用Diff算法)
  3. 仅对实际DOM应用必要的更改

与直接操作DOM相比的优势

  • 减少直接操作DOM的次数
  • 通过批量更新提高性能
  • 优化了渲染效率

14. React中如何实现代码分割(Code Splitting)?

解答
在React中,可以使用动态导入(Dynamic Import)和React.lazy结合Suspense来实现代码分割。

实现方式

1
2
3
4
5
6
7
8
9
const MyComponent = React.lazy(() => import('./MyComponent'));

function MyPage() {
return (
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
);
}

优势

  • 提高初始加载性能
  • 按需加载组件
  • 减少首屏加载时间

15. React中如何优化性能?

解答
React性能优化技巧包括:

  1. 使用React.memo:避免不必要的组件重新渲染

    1
    2
    3
    const MyComponent = React.memo((props) => {
    // 组件逻辑
    });
  2. 使用useMemo:缓存计算结果,避免重复计算

    1
    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  3. 使用useCallback:缓存函数引用,避免不必要的重新创建

    1
    2
    3
    const memoizedCallback = useCallback(() => {
    doSomething(a, b);
    }, [a, b]);
  4. 代码分割:按需加载组件

  5. 虚拟化列表:使用react-window或react-virtualized处理大数据列表

六、其他高级话题

16. 什么是Context API?它的优缺点是什么?

解答
Context API用于在组件树中共享全局数据,避免了通过层层传递props的繁琐操作。

优点

  • 方便在深层次的组件中获取全局数据
  • 减少了中间组件传递props的代码量
  • 实现全局数据的集中管理和更新

缺点

  • 如果数据发生变化,所有使用该Context的组件都会重新渲染,可能导致不必要的性能开销
  • 容易导致数据的滥用,使组件之间的耦合度增加,不利于代码的维护和理解

17. React中如何处理事件?

解答
在React中,事件处理遵循以下原则:

  1. 使用合成事件:React使用合成事件(SyntheticEvent)封装原生事件,提供跨浏览器的兼容性
  2. 使用驼峰命名:如onClick、onSubmit,而非onclick、onsubmit
  3. 使用事件委托:React将事件处理程序添加到根节点,提高性能

示例

1
2
3
4
5
6
7
8
9
10
11
function App() {
const handleClick = (event) => {
console.log('Button clicked', event);
};

return (
<button onClick={handleClick}>
Click me
</button>
);
}

18. 什么是React的Concurrent Mode?

解答
Concurrent Mode是React 18引入的特性,允许React在渲染UI时保持应用响应性。

核心优势

  • 优先处理用户交互,避免UI卡顿
  • 通过Suspense实现更平滑的加载状态
  • 支持渐进式渲染,提高应用性能

实现方式

1
2
3
4
5
// 在React 18中,使用createRoot替代render
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'));
root.render(<App />);

七、实战经验

19. 在使用Ant Design时,是否做过组件二次封装?比如对Table或Form组件的封装思路是什么?

解答
是的,做过二次封装。以Table组件为例,针对多个页面需重复配置”分页、加载状态、空数据提示、固定列”等共性需求,封装为BaseTable组件:

封装思路

  1. 接收columns(列配置)、dataSource(数据源)、loading(加载状态)等props
  2. 内部默认实现分页逻辑(如pagination={ { pageSize: 10, showSizeChanger: true } })
  3. 统一空数据提示(通过locale属性自定义”暂无数据”文案,避免各页面重复写emptyText)
  4. 暴露onChange事件,支持页面自定义表格交互(如排序、筛选后的回调)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const BaseTable = ({ columns, dataSource, loading, ...rest }) => {
return (
<Table
columns={columns}
dataSource={dataSource}
loading={loading}
pagination={{
pageSize: 10,
showSizeChanger: true,
locale: { emptyText: '暂无数据' }
}}
{...rest}
/>
);
};

20. 什么是”道具钻孔”(Props Drilling)?如何避免它?

解答
道具钻孔是指数据需要通过多个中间组件传递,才能到达需要该数据的组件。

问题

  • 代码冗余
  • 组件耦合度高
  • 维护困难

解决方法

  1. Context API:用于全局数据共享
  2. 状态管理库:如Redux、MobX
  3. 自定义Hooks:封装数据获取和状态逻辑

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 道具钻孔(不推荐)
<Parent>
<ChildA>
<ChildB>
<ChildC data={data} />
</ChildB>
</ChildA>
</Parent>

// 使用Context API(推荐)
const MyContext = createContext();

function Parent() {
const [data, setData] = useState(...);

return (
<MyContext.Provider value={data}>
<ChildA />
</MyContext.Provider>
);
}

function ChildC() {
const data = useContext(MyContext);
// 使用data
}


React面试题目
https://zjw93615.github.io/2025/12/06/React/React面试题目/
作者
嘉炜
发布于
2025年12月6日
许可协议