前端面试之 React 50题及参考答案
本文将为你整理 50 道 React 高频面试题及其详细答案,涵盖核心概念、组件、状态管理、路由、性能优化等方面。无论是初学者还是经验丰富的开发者,都能从中获得深入的知识点和面试技巧。
🔍 1. React 基础概念
1.1 什么是 React?它的核心概念是什么?
React 是 Facebook 于 2011 年开发的前端 JavaScript 库,用于构建用户界面。它遵循基于组件的方法,有助于构建可重用的 UI 组件,并用于开发复杂的交互式 Web 和移动 UI。
核心概念:
- 组件化:将 UI 拆分为独立、可复用的组件。
- 虚拟 DOM:使用虚拟 DOM 来提高性能。
- 单向数据流:数据从父组件流向子组件,保持可预测性。
1.2 React 有哪些特征?
React 的主要特征包括:
- 使用虚拟 DOM 而不是真实 DOM。
- 支持服务器端渲染。
- 遵循单向数据流或数据绑定。
- 声明式设计,使代码更易于理解和调试。
- 一切皆组件,代码模块化程度高。
1.3 React 的优势是什么?
React 的优势包括:
- 高性能:通过虚拟 DOM 减少直接操作真实 DOM 的开销。
- 跨浏览器兼容:虚拟 DOM 提供了标准化的 API,甚至在 IE8 中也能工作。
- 组件化:代码模块化,重用更容易,可维护性高。
- 单向数据流:使状态管理更加可预测。
- 同构 JavaScript:支持服务端渲染,有助于 SEO。
- 丰富的生态系统:与多种工具和框架集成良好。
1.4 React 的局限性是什么?
React 的局限性包括:
- 只是一个库,不是完整的框架:需要与其他库搭配使用以构建完整应用。
- 学习曲线较陡:特别是对于新手,理解 JSX、组件生命周期等概念可能需要时间。
- 编码复杂性:使用内联模板和 JSX 可能使代码变得复杂。
1.5 什么是 JSX?
JSX 是 JavaScript XML 的简写,是 React 使用的一种语法扩展,它允许在 JavaScript 中编写类似 HTML 的结构。例如:
render() {
return (
<div>
<h1>Hello World from Edureka!!</h1>
</div>
);
}
JSX 通过 Babel 等工具转换为 JavaScript 对象,以便浏览器能够解析。
1.6 浏览器为什么不能直接读取 JSX?
浏览器只能解析 JavaScript 对象,而不能直接读取 JSX。因此,需要使用 Babel 等转换器将 JSX 转换为 JavaScript 对象,然后传递给浏览器执行。
1.7 与 ES5 相比,React 的 ES6 语法有何不同?
ES6 引入了许多新特性,在 React 开发中常见的变化包括:
模块导入导出:
// ES5 var React = require('react'); // ES6 import React from 'react';
组件定义:
// ES5 var MyComponent = React.createClass({ render: function() { return <h3>Hello Edureka!</h3>; } }); // ES6 class MyComponent extends React.Component { render() { return <h3>Hello Edureka!</h3>; } }
状态和属性管理:
// ES5 var App = React.createClass({ getInitialState: function() { return { name: 'world' }; }, render: function() { return <h3>Hello, {this.state.name}!</h3>; } }); // ES6 class App extends React.Component { constructor() { super(); this.state = { name: 'world' }; } render() { return <h3>Hello, {this.state.name}!</h3>; } }
1.8 React 与 Angular 有何不同?
对比项 | React | Angular |
---|---|---|
架构 | 只有 MVC 的视图 | 完整的 MVC |
渲染 | 服务器端渲染 | 客户端渲染 |
DOM | 使用虚拟 DOM | 使用真实 DOM |
数据绑定 | 单向数据绑定 | 双向数据绑定 |
调试 | 编译时调试 | 运行时调试 |
作者 |
React 更灵活,专注于视图层,而 Angular 是一个完整的框架,提供了更多内置功能。
1.9 什么是虚拟 DOM?解释其工作原理。
虚拟 DOM 是一个轻量级的 JavaScript 对象,它是真实 DOM 的副本。虚拟 DOM 的工作原理分为三个步骤:
- 生成虚拟 DOM树:当组件状态变化时,React 会在虚拟 DOM 中重新渲染整个 UI。
- Diff 计算:React 会比较新旧虚拟 DOM 树的差异。
- 更新真实 DOM:计算完成后,React 只会更新真实 DOM 中发生变化的部分。
这种方式避免了频繁操作真实 DOM,从而提高了性能。
1.10 为什么虚拟 DOM 会提高性能?
虚拟 DOM 相当于在 JavaScript 和真实 DOM 之间加了一个缓存层。通过 DOM Diff 算法,可以避免不必要的 DOM 操作,从而提升性能。具体来说:
- 用 JavaScript 对象结构表示 DOM 树的结构。
- 用这个树构建一个真正的 DOM 树,插入文档中。
- 当状态变更时,重新构造一棵新的对象树。
- 比较新旧两棵树的差异。
- 将差异应用到真正的 DOM 树上,视图更新。
⚛️ 2. React 组件
2.1 如何理解“在 React 中,一切都是组件”?
组件是 React 应用程序 UI 的构建块。它们将整个 UI 拆分成独立且可重用的小块,每个组件都可以独立渲染,而不会影响 UI 的其余部分。例如,一个购物网站可能由导航栏组件、商品列表组件、购物车组件等构成。
2.2 React 中 render()
的目的是什么?
每个 React 组件都必须有一个 render()
方法。它返回一个 React 元素,该元素是原生 DOM 组件的表示方式。如果需要渲染多个 HTML 元素,必须将它们组合在一个封闭标签(如 <div>
)中。此函数必须保持纯净,即每次调用时都返回相同的结果。
2.3 如何将两个或多个组件嵌入到一个组件中?
可以通过在父组件的 render
方法中引入子组件来实现。例如:
class MyComponent extends React.Component {
render() {
return (
<div>
<h1>Hello</h1>
<Header />
</div>
);
}
}
class Header extends React.Component {
render() {
return <h1>Header Component</h1>;
}
}
ReactDOM.render(<MyComponent />, document.getElementById('content'));
2.4 什么是 Props?
Props 是 React 中 Properties 的简写,是只读的数据,从父组件传递给子组件。它们必须保持纯净(不可变),这有助于维护单向数据流,通常用于呈现动态生成的数据。
2.5 React 中的 state 是什么?它是如何使用的?
State 是 React 组件的核心,是决定组件渲染和行为的对象。与 Props 不同,State 是可变的,用于创建动态和交互式组件。State 通过 this.state
访问,并通过 this.setState()
更新。
2.6 对比 state 和 props 的区别。
条件 | State | Props |
---|---|---|
从父组件接收初始值 | 是 | 是 |
父组件可以改变值 | 否 | 是 |
在组件内部设置默认值 | 是 | 是 |
组件内部变化 | 是 | 否 |
为子组件设置初始值 | 是 | 是 |
子组件内部的变化 | 否 | 是 |
State 由组件自身管理且可变,而 Props 由父组件传递且对子组件为只读。
2.7 如何更新组件的状态?
使用 this.setState()
来更新组件的状态。例如:
class MyComponent extends React.Component {
constructor() {
super();
this.state = {
name: 'Maxx',
id: '101'
};
}
render() {
setTimeout(() => {
this.setState({ name: 'Jaeha', id: '222' });
}, 2000);
return (
<div>
<h1>Hello {this.state.name}</h1>
<h2>Your Id is {this.state.id}</h2>
</div>
);
}
}
2.8 React 中的箭头函数是什么?它是如何使用的?
箭头函数是编写函数表达式的简短语法,也称为“胖箭头”(=>)函数。它们允许正确绑定组件的上下文,因为在 ES6 中自动绑定默认不可用。例如:
// General way
render() {
return (<MyInput onChange={this.handleChange.bind(this)} />);
}
// With Arrow Function
render() {
return (<MyInput onChange={(e) => this.handleOnChange(e)} />);
}
箭头函数在处理高阶函数时非常有用。
2.9 如何区分有状态组件和无状态组件?
特征 | 有状态组件 | 无状态组件 |
---|---|---|
存储状态变化信息 | 是 | 否 |
计算内部状态 | 是 | 否 |
有权改变状态 | 是 | 否 |
包含过去、现在、未来状态 | 是 | 否 |
通信方式 | 将 Props 发送给无状态组件 | 从有状态组件接收 Props |
有状态组件负责管理状态,而无状态组件主要负责展示。
2.10 React 组件生命周期的不同阶段是什么?
React 组件的生命周期分为三个阶段:
- 初始渲染阶段:组件即将开始其生命旅程并进入 DOM。
- 更新阶段:组件被添加到 DOM 后,可能因 Props 或 State 变化而更新和重新渲染。
- 卸载阶段:组件被销毁并从 DOM 中移除。
2.11 解释 React 组件的生命周期方法。
常用的生命周期方法包括:
componentWillMount()
:在客户端和服务器端渲染之前执行。componentDidMount()
:仅在第一次渲染后在客户端执行。componentWillReceiveProps()
:当组件从父组件接收到新的 Props 时调用。shouldComponentUpdate()
:根据特定条件返回 true 或 false,决定组件是否需要更新。componentWillUpdate()
:在 DOM 中进行渲染之前调用。componentDidUpdate()
:在渲染发生后立即调用。componentWillUnmount()
:在组件从 DOM 中卸载后调用,用于清理内存空间。
2.12 React 中的事件是什么?
在 React 中,事件是对鼠标悬停、鼠标单击、按键等特定操作的触发反应。事件处理类似于 DOM 元素中的事件,但有一些语法差异:
- 事件使用驼峰命名(如
onClick
),而不是纯小写。 - 事件作为函数而不是字符串传递。
- Event 参数包含一组特定于事件的属性。
2.13 如何在 React 中创建事件?
在 JSX 中,可以将事件处理函数作为属性传递给元素。例如:
class Display extends React.Component {
show(evt) {
// 处理事件的代码
}
render() {
return (
<div onClick={this.show}>
Click Me!
</div>
);
}
}
2.14 React 中的合成事件是什么?
合成事件是充当浏览器本地事件的跨浏览器包装器的对象。它们将不同浏览器的行为组合成一个 API,确保事件在不同浏览器中显示一致的属性。
2.15 如何理解 React 中的 refs?
Refs 是 React 中 References 的简写,用于存储对特定 React 元素或组件的引用。Refs 由组件的渲染配置函数返回,用于获取对 render() 返回的特定元素或组件的引用。它们在需要 DOM 测量或向组件添加方法时非常有用。例如:
class ReferenceDemo extends React.Component {
display() {
const name = this.inputDemo.value;
document.getElementById('disp').innerHTML = name;
}
render() {
return (
<div>
Name: <input type="text" ref={input => this.inputDemo = input} />
<button name="Click" onClick={this.display}>Click</button>
<h2>Hello <span id="disp"></span> !!!</h2>
</div>
);
}
}
2.16 列出一些应该使用 Refs 的情况。
Refs 应该用于以下情况:
- 管理焦点、选择文本或媒体播放。
- 触发命令式动画。
- 与第三方 DOM 库集成。
2.17 如何在 React 中模块化代码?
可以通过使用 export
和 import
属性来模块化代码,将组件分别编写在不同的文件中。例如:
// ChildComponent.jsx
export default class ChildComponent extends React.Component {
render() {
return (
<div>
<h1>This is a child component</h1>
</div>
);
}
}
// ParentComponent.jsx
import ChildComponent from './childcomponent.js';
class ParentComponent extends React.Component {
render() {
return (
<div>
<ChildComponent />
</div>
);
}
}
2.18 React 中的表单是如何创建的?
React 表单类似于 HTML 表单,但表单数据由 React 组件的 state 管理,只能通过 setState()
更新。例如:
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
2.19 如何理解受控组件和非受控组件?
特征 | 受控组件 | 非受控组件 |
---|---|---|
状态管理 | 由 React 组件 state 管理 | 由 DOM 管理 |
数据流 | 通过 Props 获取当前值 | 使用 Refs 获取当前值 |
变化通知 | 通过回调通知变化 | 直接操作 DOM |
受控组件的数据由 React 控制,而非受控组件的数据由 DOM 自身管理。
2.20 什么是高阶组件(HOC)?
高阶组件(HOC)是重用组件逻辑的高级方法,它是一个函数,接受一个组件并返回一个新组件。HOC 源自 React 的组合性质,可以用于代码重用、逻辑和引导抽象、状态抽象和操作 Props。
2.21 高阶组件可以做些什么?
HOC 可以用于:
- 代码重用、逻辑和引导抽象。
- 渲染劫持。
- 状态抽象和操作。
- Props 操纵。
2.22 什么是纯组件?
纯组件是 React 中最简单和最快的组件类型,它们可以替换任何只有 render()
的组件。纯组件增强了代码的简单性和应用程序的性能。
2.23 Keys 在 React 中的意义是什么?
Keys 用于识别独特的虚拟 DOM 元素及其驱动 UI 的相应数据。它们帮助 React 通过回收 DOM 中的所有现有元素来优化渲染。Keys 必须是唯一的数字或字符串,React 使用它们来重新排序元素而不是重新渲染它们,从而提高应用程序性能。
2.24 为什么在 React 中遍历时不建议使用索引作为唯一的 key 值?
虽然有时使用索引作为 key 是可行的,但如果列表可以重新排序,可能会导致性能降低或状态错误。因此,最好使用一个唯一且稳定的标识符作为 key,而不是索引。
2.25 React 中类组件和函数组件有什么区别?
对比项 | 类组件 | 函数组件 |
---|---|---|
定义方式 | 使用 ES6 类,继承 React.Component | 使用 JavaScript 函数 |
状态管理 | 可以使用内部状态(state)和生命周期方法 | 原本无状态,但 React 16.8 引入 Hooks 后可以 |
生命周期 | 有完整的生命周期方法 | 通过 useEffect 等 Hooks 模拟生命周期 |
使用 this | 需要 | 不需要 |
代码简洁性 | 相对复杂 | 更简洁,易于测试和理解 |
自从 React 16.8 引入 Hooks 后,函数组件的能力大大增强,现在几乎可以完成类组件的所有功能。
2.26 除了在构造函数中绑定 this,还有其它方式吗?
是的,除了在构造函数中绑定 this
,还可以使用:
- 属性初始值设定项(Property Initializers):在类属性中使用箭头函数。
- 箭头函数在回调中:但注意每次渲染都会创建新的回调函数。
例如:
// 属性初始值设定项
class MyComponent extends React.Component {
handleClick = () => {
// 使用 this
}
}
// 箭头函数在渲染中
render() {
return <button onClick={() => this.handleClick()}>Click</button>;
}
2.27 在构造函数中调用 super(props)
的目的是什么?
在 ES2015 中,子类必须在 constructor
中调用 super()
后才能使用 this
。传递 props
给 super()
的原因则是便于在子类的 constructor
中访问 this.props
。
2.28 React 中 Element
和 Component
有何区别?
- React Element:是描述屏幕上所见内容的数据结构,是对于 UI 的对象表述。典型的 React Element 是利用 JSX 构建的声明式代码片,然后被转化为
createElement
的调用组合。 - React Component:是一个函数或一个类,可以接收参数输入,并且返回某个 React Element。
2.29 什么是纯组件(PureComponent)?
PureComponent
是 React 提供的一个优化性能的组件基类。它通过对 props
和 state
进行浅比较来实现 shouldComponentUpdate
方法,从而避免不必要的渲染。
2.30 除了在构造函数中绑定 this,还有其它方式吗?
是的,除了在构造函数中绑定 this
,还可以使用:
- 属性初始值设定项(Property Initializers):在类属性中使用箭头函数。
- 箭头函数在回调中:但注意每次渲染都会创建新的回调函数。
例如:
// 属性初始值设定项
class MyComponent extends React.Component {
handleClick = () => {
// 使用 this
}
}
// 箭头函数在渲染中
render() {
return <button onClick={() => this.handleClick()}>Click</button>;
}
🧩 3. React Hooks
3.1 什么是 React Hooks?它解决了什么问题?
React Hooks 是 React 16.8 引入的一种新特性,允许在函数组件中使用状态和其他 React 特性。它解决了类组件在逻辑复用和状态管理上的一些限制,使函数组件的能力大大增强。
3.2 React Hook 的使用限制有哪些?
使用 React Hooks 时需要遵守以下规则:
- 只在最顶层使用 Hook:不要在循环、条件判断或嵌套函数中调用 Hook。
- 只在 React 函数中调用 Hook:不要在普通的 JavaScript 函数中调用 Hook。
- 在自定义 Hook 中使用 Hook:可以在自定义 Hook 中调用其他 Hook。
这些规则保证了 React 能正确追踪各个 Hook 的状态。
3.3 常用的 React Hooks 有哪些?
useState
:用于在函数组件中添加状态。useEffect
:用于处理副作用,如数据获取、订阅等。useContext
:用于访问 React 的 Context。useReducer
:作为useState
的替代方案,用于更复杂的状态逻辑。useCallback
:缓存回调函数,避免不必要的重新渲染。useMemo
:缓存计算结果,避免重复计算。useRef
:用于获取 DOM 节点或存储可变值。
3.4 如何使用 useState
Hook?
useState
返回一个状态值和一个更新该状态的函数。例如:
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
3.5 如何使用 useEffect
Hook?
useEffect
用于处理副作用操作,如数据获取、设置订阅等。它接受两个参数:一个函数和一个可选的依赖数组。例如:
import React, { useState, useEffect } from 'react';
function Example() {
const [data, setData] = useState([]);
useEffect(() => {
// 获取数据
fetchData().then(data => setData(data));
}, []); // 空数组表示只在组件挂载时执行一次
return (
<div>
{/* 渲染数据 */}
</div>
);
}
🔄 4. React 状态管理
4.1 MVC 框架的主要问题是什么?
MVC 框架的主要问题包括:
- DOM 操作非常昂贵。
- 应用程序缓慢且效率低下。
- 存在巨大的内存浪费。
- 由于循环依赖,围绕模型和视图创建了复杂的模型。
4.2 解释一下 Flux。
Flux 是一种架构模式,它强制执行单向数据流。它控制派生数据并使用对所有数据具有权限的中央存储实现多个组件之间的通信。整个应用程序中的任何数据更新都必须仅在此处进行。Flux 为应用程序提供稳定性并减少运行时错误。
4.3 什么是 Redux?
Redux 是 JavaScript 应用程序的可预测状态容器,用于整个应用程序状态管理。使用 Redux 开发的应用程序易于测试,并且可以在不同环境中运行,表现出一致的行为。
4.4 Redux 遵循的三个原则是什么?
Redux 遵循三个基本原则:
- 单一数据源:整个应用程序的状态存储在单个存储中的对象/状态树中。
- 状态是只读的:改变状态的唯一方法是触发一个动作。
- 使用纯函数进行更改:为了指定状态树如何通过操作进行转换,你需要编写纯 reducer。
4.5 Redux 中间件是怎么拿到 store 和 action?然后怎么处理?
Redux 中间件的基本形式是一个函数,该函数返回另一个函数,再返回一个函数。例如:
const middleware = store => next => action => {
// 在这里处理你的代码
};
store
:Redux 的 store 实例,可以调用store.getState()
获取当前状态,或store.dispatch()
派发新 action。next
:一个函数,调用next(action)
将控制权交给下一个中间件。action
:当前派发的 action。
中间件可以在 action 被传递到 reducer 之前修改或拦截它。
4.6 Redux 有什么缺点?
Redux 的缺点包括:
- 一个组件所需要的数据必须由父组件传递过来,而不能像 Flux 中那样直接从 store 取。
- 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新 render,可能会影响效率,或者需要编写复杂的
shouldComponentUpdate
进行判断。
🚀 5. React 性能优化
5.1 如何优化 React 应用的性能?
优化 React 应用性能的常见方法包括:
- 使用
shouldComponentUpdate
或React.memo
:避免不必要的组件渲染。 - 使用虚拟列表技术:如
react-virtualized
,优化大型列表的渲染。 - 合理使用生命周期方法:管理资源的加载和释放。
- 代码分割和懒加载:减小初始加载的文件大小。
- 使用浏览器开发工具进行性能分析:根据分析结果进行针对性优化。
5.2 shouldComponentUpdate
是做什么的?
shouldComponentUpdate
是 React 的生命周期方法,用于判断组件是否需要重新渲染。通过返回 true
或 false
,可以控制组件是否更新,从而避免不必要的渲染,提高性能。
5.3 为什么使用虚拟 DOM 会提高性能?
虚拟 DOM 相当于在 JavaScript 和真实 DOM 之间加了一个缓存层。通过 DOM Diff 算法,可以避免不必要的 DOM 操作,从而提升性能。
5.4 React 很多个 setState
为什么是执行完再 render?
React 为了提高性能,会将一次渲染周期中的多个 setState
调用合并起来,之后再一次性地渲染。这样可以避免频繁操作 DOM,提高渲染性能。
🧪 6. React 测试与调试
6.1 如何在 React 中进行测试?
React 应用程序可以通过多种方式进行测试:
- Jest:用于 JavaScript 测试框架,常用于 React 应用程序的单元测试。
- React Testing Library:用于测试 React 组件,更注重用户体验。
- Enzyme:用于测试 React 组件的输出和行为。
6.2 React 中的调试技巧有哪些?
- 使用 React Developer Tools:浏览器扩展程序,用于检查 React 组件树和状态。
- Console Logging:在代码中添加
console.log
语句进行调试。 - Debugger 语句:在代码中插入
debugger
语句,以便在浏览器开发者工具中调试。
📦 7. React 生态系统
7.1 React Router 是什么?
React Router 是 React 的标准路由库,用于管理 React 应用程序中的导航和路由。它提供了一种声明式的方式来定义路由和导航。
7.2 什么是 Next.js?
Next.js 是一个 React 框架,提供了生产环境所需的所有功能以及最佳的开发体验。它包括静态及服务器端融合渲染、支持 TypeScript、智能化打包、路由预取等功能,无需任何配置。
7.3 React 项目常用的脚手架有哪些?
React 项目常用的脚手架包括:
- Create React App:官方支持的创建 React 应用程序的方式。
- Yeoman:用于生成现代 Web 应用程序的脚手架工具。
- umi.js:可插拔的企业级 React 应用框架。
🎯 总结
本文整理了 50 道 React 高频面试题及其详细答案,涵盖了 React 的核心概念、组件、状态管理、生命周期、Hooks、性能优化等方面。希望这些内容能帮助你更好地准备 React 技术面试,深入理解 React 的工作原理和最佳实践。
关键知识点回顾
- React 基础:理解 React 的特点、优势、局限性以及虚拟 DOM 的工作原理。
- 组件开发:掌握类组件和函数组件的区别、状态和属性的管理、事件处理以及组件的生命周期。
- 状态管理:熟悉 React 的 state 和 props,以及 Redux 和 Flux 的状态管理方案。
- 性能优化:了解如何通过 shouldComponentUpdate、虚拟 DOM 等技术优化 React 应用性能。
- 生态系统:熟悉 React Router、Next.js 等常用工具和框架。