前端面试题准备 - React
什么是 React?
React 是一个简单的 javascript UI 库,用于构建高效、快速的用户界面。它是一个轻量级库,因此很受欢迎。它遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效。它使用虚拟 DOM 来有效地操作 DOM。它遵循从高阶组件到低阶组件的单向数据流。
React 生命周期
React 组件的生命周期可以分为三个阶段:
- 挂载阶段(Mounting)
当 React 首次渲染元件,并把渲染后的节点加入 DOM 中时,我们称这时为 mounting。当 React 把节点加入到 DOM 后,浏览器会渲染并绘制画面。在这个阶段,生命周期将会依照下列的顺序呼叫这些方法:
constructor(props)getDerivedStateFromProps(props, state)render()componentDidMount()
- 更新阶段(Updating)
在 mounting 后,如果一个元件的 prop 或 state 有变化时,就会触发重新渲染。重新渲染后,React 会去比较虚拟 DOM 有哪些地方改变了,然后将改变的部分更新到实际的 DOM 上。这个阶段我们称它为 updating。当一个 component 被重新渲染时,其生命周期将会依照下列的顺序呼叫这些方法:
getDerivedStateFromProps(props, state)shouldComponentUpdate(nextProps, nextState)render()getSnapshotBeforeUpdate(prevProps, prevState)componentDidUpdate(prevProps, prevState, snapshot)
- 卸载阶段(Unmounting)
当一个元件被从 DOM 中移除时,我们称之为 unmounting。这时将会呼叫:
componentWillUnmount()
React 中发起网络请求应该在哪个生命周期中进行?为什么?
在 componentDidMount 中发起网络请求。因为 componentDidMount 是在组件挂载到 DOM 后执行的,所以在这个生命周期中发起网络请求可以确保在组件挂载后执行。
state 和 props 触发更新的生命周期分别?
state 和 props 触发更新的生命周期相同。但是 getDerivedStateFromProps、shouldComponentUpdate、getSnapshotBeforeUpdate 这几个周期中 如果是更新 state,参数 prevState 会有值,nextProps 是一个空对象; 如果是更新 props,参数 nextProps 会有值,prevState 是一个空对象。
虚拟 DOM 的原理是什么?
虚拟 DOM(Virtual DOM,简称 VDOM)是一种 轻量级的 JavaScript 对象,用于模拟真实 DOM 结构。它是 React、Vue 等前端框架中用于 优化页面渲染性能 的关键技术。
当页面状态(state)发生变化时,前端框架不会直接修改真实 DOM,而是先在虚拟 DOM 中进行计算,然后通过 Diff 算法 找到最小变更,并批量更新真实 DOM,提高性能。
更新过程一般分为 3 个阶段:
- 创建虚拟 DOM(VNode)
组件初次渲染时,会生成对应的虚拟 DOM 树(VDOM)。
这棵 VDOM 仅仅是一个 JavaScript 对象,不会影响真实 DOM。
- Diff 计算(对比新旧 VDOM)
当组件的 state 或 props 发生变化,框架会重新生成新的虚拟 DOM。
通过 Diff 算法,比较新旧虚拟 DOM,找出变化的部分。
- 更新真实 DOM(Reconciliation 过程)
根据 Diff 计算结果,框架 最小化真实 DOM 的变更,只更新必要部分。
使用 批量更新(如 React 的 Fiber 机制)提高性能。
虚拟 DOM 的优缺点:
优点:
- 提升性能:通过 Diff 算法 计算最小更新范围,减少不必要的 DOM 操作,避免频繁重绘和重排(Reflow & Repaint)。React 采用 批量更新 和 Fiber 架构,进一步优化渲染。
- 跨平台渲染:由于 VDOM 只是一个 JavaScript 对象,它不仅能映射到浏览器 DOM,还能用于 React Native(移动端)、Server-Side Rendering(SSR)等。
- 代码简洁、易维护:通过 声明式 UI(Declarative UI),开发者只需关注 数据状态,而不必手动操作 DOM,提高开发效率。
缺点:
- 初次渲染比原生 DOM 慢:由于需要 创建虚拟 DOM 并 计算 Diff,对于静态页面(如普通 HTML)可能反而带来额外开销。
- Diff 计算的性能开销:Diff 算法虽然高效,但仍然需要一定的 CPU 计算,如果状态频繁变更,可能影响性能(如复杂的动画、大量节点更新)。
什么是 JSX?
JSX(JavaScript XML) 是一种 JavaScript 语法扩展,用于在 React 中编写类似 HTML 的代码。它允许开发者在 JavaScript 代码中直接编写 UI 结构,提高可读性和开发效率。
JSX 不会直接被浏览器执行,它需要通过 Babel 转换为标准 JavaScript,本质上是 React.createElement() 的语法糖。
React 组件的通信方式
- 父组件向子组件通信
父组件通过 props 向子组件传递数据。
- 子组件向父组件通信
子组件通过 回调函数 向父组件传递数据。
- 跨级组件通信
props传递:可能会出现多个层级,需要逐层传递,增加了复杂度。很可能这些props并不是中间组件需要的,只是为了传递给子组件。context传递:context相当是一个大容器,可以把要通信的内容放在这个容器中,不管嵌套多深,都可使用。对于跨域多层的全局数据可以使用context实现。
- 非嵌套组件通信
- 发布订阅:创建一个全局的
event bus(事件总线),然后通过on和emit来传递消息。 redux等全局状态管理工具:通过Redux、Zustand等全局状态管理工具来传递消息。
为什么 React 渲染列表时需要加上 key?
在 React 中,渲染列表时需要为每个元素提供唯一的 key,其主要作用是 优化性能、减少不必要的 DOM 操作,并确保 React 正确识别组件的变化。
React 使用 Diff 算法 来比较 新旧虚拟 DOM(VDOM),找出需要更新的部分。如果没有 key,React 只能按元素索引 进行 Diff 计算。当列表项顺序发生变化(如插入、删除、排序),React 可能会 错误更新或不必要地重新渲染。
props 和 state 的区别
props 是一个从外部传进组件的参数,主要作为就是从父组件向子组件传递数据,它具有可读性和不变性,只能通过外部组件主动传入新的 props 来重新渲染子组件,否则子组件的 props 以及展现形式不会改变。
state 的主要作用是用于组件保存、控制以及修改自己的状态,它只能在 constructor 中初始化,它算是组件的私有属性,不可通过外部访问和修改,只能通过组件内部的 this.setState 来修改,修改 state 属性会导致组件的重新渲染。
区别:
props是外部传入的,state是组件内部维护的。props具有可读性和不变性,state具有可变性和可读性。props主要用于组件之间传递数据,state主要用于组件内部状态管理。
React 单向数据流
单向数据流是指数据的流向只能由父组件通过 props 将数据传递给子组件,不能由子组件向父组件传递数据,要想实现数据的双向绑定,只能由子组件接收父组件 props 传过来的方法去改变父组件的数据,而不是直接将子组件的数据传递给父组件。
如何理解 setState
setState 是 React 中用于更新组件状态的函数。当 setState 被调用时,React 会根据新的状态触发重新渲染,以反映状态变化。
- 异步更新
setState 是异步的,它不会立即更新状态,而是将更新状态的操作放入队列中,等当前事件处理完成后再执行。这是为了优化性能,避免频繁的更新状态。
- 合并更新
setState 会将传入的部分状态与当前状态合并。这意味着它不会完全覆盖当前状态,而是仅更新改变的部分。
- 批量更新
React 会批量更新状态,尤其在事件处理和生命周期方法中,React 会将多个 setState 调用合并成一个更新,以提高性能。
1 | handleClick = () => { |
React Fiber
React Fiber 是 React 16 引入的一种新的调度算法,用于优化 React 的渲染过程。它将渲染过程拆分为多个小任务,并使用时间片来分配任务,从而提高渲染效率。
核心概念:
- 可中断渲染:在 Fiber 之前,React 的渲染过程一旦开始就无法中断,这被称为”同步渲染”。当组件树很大时,这会导致主线程被长时间占用,造成用户交互、动画等卡顿。Fiber 引入了可中断的渲染过程,允许 React 根据优先级暂停和恢复渲染工作。
- 优先级调度:Fiber 允许根据任务的重要性分配不同的优先级。例如,用户交互响应的优先级会高于数据更新的渲染。
- 增量渲染:将渲染工作分解成多个小单元,而不是一次完成整个组件树的渲染。
- 时间片:React 将渲染工作分解成小单元,每个单元执行完后,会检查是否有剩余时间继续工作,如果没有,就会让出控制权给浏览器。
React Fiber 的实现原理:
- Fiber 节点结构:Fiber 是一种链表结构,每个 Fiber 节点对应一个 React 元素,包含了组件的类型、状态、副作用等信息。
- 双缓冲技术:React 使用”双缓冲”技术,维护两棵树,一颗是
current树,另一颗是workInProgress树。current树是当前屏幕上显示的内容对应的 Fiber 树,workInProgress树是正在构建的新 Fiber 树。这种机制允许 React 在后台构建新的树,而不影响当前显示的内容。当 workInProgress 树构建完成后,React 通过简单地切换指针使其成为新的 current 树。
React Hooks解决了什么问题?
1. 组件逻辑复用困难
在 Hooks 之前,组件逻辑复用通常通过高阶组件(HOC)或渲染属性(Render Props)来实现。然而,这些方法会导致组件嵌套过深,难以维护。
2. 复杂组件难以理解
类组件中,相关逻辑可能分散在不同的生命周期方法中,导致代码难以阅读和理解。
3. this 的困惑
类组件中,this 的指向容易让人困惑。还需要手动绑定事件处理函数。
总的来说,类组件在多年的应用实践中,发现了很多无法避免问题而又难以解决。而相对类组件,函数组件又太过于简陋,比如没有状态,没有生命周期,所以 React Hooks 就是为了解决这些问题而出现的。它使得函数组件可以拥有状态,可以拥有生命周期,可以拥有组件的上下文,可以拥有组件的 ref 等等,这样就不必将函数组件转换为类组件。
Hooks 的使用限制
- 只能在函数组件中使用
- 不能在循环、条件、嵌套函数中使用
原因:
Hooks 依赖于调用顺序来正确工作。React 内部维护了一个”链表”来跟踪每个组件中的所有 Hooks。每次组件渲染时,React 期望 Hooks 以完全相同的顺序被调用。如果将 Hooks 放在条件语句、循环或嵌套函数中,React 将可能无法正确地匹配 Hooks 的顺序,从而导致错误。
SSR 和 CSR 的区别
服务端渲染 (SSR, Server-Side Rendering) 是一种页面渲染的技术,它将页面的 HTML 内容在服务器端生成,然后将生成好的 HTML 页面发送到浏览器。这与 客户端渲染 (CSR, Client-Side Rendering) 不同,在客户端渲染中,浏览器加载一个空的 HTML 框架,并通过 JavaScript 动态生成和填充页面内容。
服务端渲染 (SSR) 的工作流程:
- 用户请求页面:
用户向服务器发出请求(例如输入网址访问网页)。
- 服务器生成 HTML 内容:
服务器接收到请求后,使用服务器端的模板引擎(如Next.js)根据请求的信息动态生成完整的 HTML 页面。这个过程会结合服务器上的数据和模板,生成包含动态内容的 HTML 页面。内容在服务器端渲染好后,直接以完整的 HTML 响应发送到浏览器。
- 浏览器接收 HTML 页面:
浏览器接收到完整的 HTML 页面,立即呈现页面内容给用户。由于 HTML 已经包含了所有的内容,用户可以立即看到页面的初步内容。
- 后续的客户端渲染:
服务端渲染的页面可能还包含 JavaScript 文件,这些代码会在浏览器中运行,进行客户端的交互和后续的页面更新。
SSR 优点
- 更快的首屏加载速度
因为服务端渲染直接将完整的 HTML 页面发送到浏览器,用户在页面加载的初期可以看到完整的内容。相比客户端渲染,SSR 不需要等到 JavaScript 加载并执行后才显示页面内容。
- SEO 友好
对于搜索引擎来说,SSR 比 CSR 更友好。因为搜索引擎的爬虫可以直接抓取服务端渲染的完整 HTML 页面,避免了 CSR 中依赖 JavaScript 执行后生成内容的情况。
- 更好的首屏体验
由于页面内容在服务器端已经渲染好并发送到浏览器,用户可以更快地看到页面上的内容,避免了空白页面和加载延迟,提高了用户体验。
- 适合内容较为静态的应用
对于内容变化不频繁的应用,SSR 是一个非常合适的选择,可以减少客户端的计算压力,将大部分渲染任务交给服务器来处理。
SSR 缺点
- 服务器负担较重
由于页面渲染是在服务器端进行的,服务器需要处理每个请求并生成 HTML 页面。对于流量较大的网站,可能会导致服务器负载增加,影响性能和响应速度。
- 缺乏交互性
初始页面是静态的,虽然 SSR 可以在服务器端渲染出完整的页面,但客户端渲染的 JavaScript 代码通常需要后续加载才能实现动态交互。例如,React 的组件渲染逻辑可能需要在客户端继续执行,才会使页面变得可交互。
- 首次加载较慢
虽然 SSR 能加速首屏渲染,但因为需要在服务器生成 HTML,首次加载的响应时间可能会更长,尤其是服务器的处理能力不足时。
SSR 和 CSR 的对比
| 特性 | 服务端渲染 (SSR) | 客户端渲染 (CSR) |
|---|---|---|
| 首屏加载时间 | 快,HTML 已经在服务器渲染完成,直接发送到浏览器 | 慢,浏览器需要加载 JavaScript 后再渲染页面 |
| SEO 支持 | 很好,搜索引擎可以抓取完整的 HTML 内容 | 较差,搜索引擎可能无法索引 JavaScript 生成的内容 |
| 服务器负担 | 较重,所有请求都需要服务器渲染页面 | 较轻,客户端渲染,服务器只负责提供静态资源 |
| 交互性 | 初始页面静态,后续需要 JavaScript 动态加载 | 动态交互,JavaScript 控制页面渲染和更新 |
| 复杂度 | 高,开发需要处理前后端渲染的一致性 | 低,通常只有前端开发,处理较简单 |

