React 常见面试问题附答案,持续更新中
本文主要记录日常面试中遇到的问题及其参考答案,如有表述有误的地方欢迎大家指出,共同进步!
技术问题
基础
1.什么是虚拟DOM?实现原理?
Virtual DOM(虚拟DOM),是由普通的 JS 对象来描述DOM对象,因为不是真实的DOM对象,所以叫 Virtual DOM。
2.类组件和函数组件之间的区别?
- 类组件有生命周期函数,函数组件中没有
- 类组件顾名思义就有类的特性,有类的三大特性,继承、封装、多态,有内部状态,而函数组件只能通过 hook 保存内部状态。
- 只能在函数组件中使用hooks
3.在 React 中如何处理事件?
React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同:
- React 事件绑定属性的命名采用驼峰式写法,而不是小写。
- 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
4.state 和 props 区别?
state是可变的,而props不可变。
5.什么是高阶组件?
高阶组件(Higher Order Components,简称:HOC)不是我们理解的React中的组件,而是一种组件逻辑复用的技术手法,他实际就是一个以组件为参数并返回一个新的的组件的函数,用于增强和优化我们的参数组件,例如我们常用到的 redux 中的connect函数,withRouter函数都是高阶组件,用于向基础组件中注入更多数据。
6.什么是JSX?
JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。
7.为什么类方法需要绑定 this?
在 React 类组件中,由于将事件处理函数引用作为回调传递后,事件处理程序方法会丢失其隐式绑定的上下文,导致 this 值会回退到默认绑定,变成 undefined。更通俗的讲,就是事件处理函数被传递后,作用域发生了改变,丢掉了原先的this。示例如下:
<button type="button" onClick={this.handleClick}>
Click Me
</button>
有人可能会问:既然是依据“默认绑定规则”,不应该指向全局对象window或global吗?
这是因为,类声明和类表达式的主体(构造函数、静态方法和原型方法)以 严格模式 执行。在严格模式下,默认绑定this会指向undefined。
8.React 中的StrictMode(严格模式)是什么?
StrictMode 是一个用以标记出应用中潜在问题的工具。就像 Fragment ,StrictMode 不会渲染任何真实的UI。它为其后代元素触发额外的检查和警告。
StrictMode目前有助于:
- 识别具有不安全生命周期的组件
- 有关旧式字符串ref用法的警告
- 检测意外的副作用
- 检测遗留 context AP
9. class组件声明周期函数有哪些?
挂载:当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
- constructor()
- static getDerivedStateFromProps()
- render()
- componentDidMount()
更新:当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
卸载:当组件从 DOM 中移除时会调用如下方法:
- componentWillUnmount()
错误处理:当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
- static getDerivedStateFromError()
- componentDidCatch()
10.React Hooks 的优缺点?
- 相比render props 和高阶组件,更容易复用组件状态逻辑
进阶
1. 遍历数组循环生成节点时需要给节点设置key属性,这个key有什么作用?
当我们需要渲染一个列表的时候,React 会存储这个列表每一项的相关信息,当我们要更新这个列表时,React 需要确定哪些项发生了改变。我们有可能增加、删除、重新排序或者更新列表项。
在 React 中采用的是 diff 算法来对比新旧虚拟节点,从而更新节点。 在交叉对比中,当新节点跟旧节点头尾交叉对比没有结果时,会根据新节点的 key 去对比旧节点数组中的 key ,从而找到相应旧节点(这里对应的是一个key => index 的map映射)。如果没找到就认为是一个新增节点。而如果没有key,那么就会采用遍历查找的方式去找到对应的旧节点。一种一个 map 映射,另一种是遍历查找。相比而言。map 映射的速度更快。
其实如果说只是文本内容改变了,不写key反而性能和效率更高,主要是因为不写key是将所有的文本内容替换一下,节点不会发生变化,而写 key 则涉及到了节点的增和删,发现旧 key 不存在了,则将其删除,新 key 在之前没有,则插入,这就增加性能的开销。当然在我们正常的开发中,这种及其简单的更新是很少见的,大部分还是复杂的内容更新,所以按大局来说还是写 key 的效率高一些,写 key 增加的这一点点的性能开销在用户的视角上时感知不到的。
所以说 key 是给每一个 vnode 的唯一 id ,可以依靠 key ,更准确更快的拿到oldVnode中对应的vnode节点,高效和准确的更新节点。
2. React 中你是怎么做性能优化的?
- 函数组件使用Memo缓存组件,类组件合理使用 PureComponent 或 shouldComponentUpdate 钩子函数,避免组件的无效更新
- 组件懒加载,按需加载,避免包体大加载慢
- 使用 React Fragments 避免额外标记
3. React 中如何做的状态管理?
4. Redux 用了什么设计模式?你用过之后感觉有什么缺点或者说不好用的地方
5. 如何理解 React 中的 refs, 有哪些实用场景?
6. React 组件之间如何通信?
- 父子组件之间 通过 props 参数传递
- Context 共享数据
- redux 全局数据共享
7. React hooks 解决了什么问题?常用的 hooks 有哪些?
8. React hooks使用有哪些注意事项
9. React useMemo 与 useCallback 的区别以及主要的应用场景?
10. useEffect 的用法, 有哪些参数,如何检测数组依赖项的变化?
11. React Hooks 闭包问题知道吗,为什么会有这个问题,如何避免?
12. React Class 组件中请求可以在 componentWillMount 中发起吗?为什么?**
可以发起,但是React推荐在componentDidMount中执行,因为它执行时DOM已加载完毕,部分依赖更新dom而发起的请求在它内部写更加合理,并且在服务端渲染时,此函数会被执行两次,而DidMount并不会在服务端执行. 16.简要说明 React Hook 中 useState 和 useEffect 的运行原理?
13.React 如何发现重渲染、什么原因容易造成重渲染、如何避免重渲染?
14.React Hook 和闭包有什么关联关系?
15.React 中 useState 是如何做数据初始化的?
16.React中的Diff细节可以讲一下吗,是所有Fiber都需要Diff吗,React做了什么优化?又可能存在什么缺陷.
17.setState是异步的吗?给出理由?
18.说一下HOC、render props?
19.说一下React中的时间分片?
20.详细描述一下React-redux和redux和React自带跨组件传参?
21.React中绑定的事件是原生的吗?不是的话和原生事件有什么差别?React是如何做到平台统一?
22.React 如何进行组件/逻辑复用?
23.什么场景需要用React Portals?
24.React 如何异步加载组件?
25.Redux 如何处理异步action,如何实现的?
场景应用问题
1.给出以下代码,x会被打印几次?
import * as React from "react";
import { useState, useEffect } from "react";
import { createRoot } from "react-dom/client";
function SetStatePage(props) {
const [ count, setCount ] = useState(-1);
useEffect(() => {
setCount(0)
});
console.log("x"); //sy-log
return (
<div>
<h3>SetStatePage</h3>
</div>
);
}
const root = createRoot(document.getElementById("root"));
root.render(<SetStatePage />);
console.log("React", React.version); //sy-log
2.看代码,log打印几次?
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom/client";
ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
function App() {
const [count, setCount] = useState(-1);
useEffect(() => {
setCount(0);
});
// 打印几次?
console.log("app render", count);
return <p>hello world {count}</p>;
}