前言
平时都用 React
开发 ,但说实话没怎么看过具体的知识点,最近在拉钩看了两门课程,觉得收获颇深,根据自己的一点浅显的理解整理了一点 React
的简单用来面试的知识点,其中很多自己都是一笔带过,课程里还有很多源码的解析,大伙可以自己购买来看
课程还在持续更新,不得不说,非常香
具体内容
1. 简单介绍下 React?
定义:
以组件化的思想,用于构建用户界面的JavaScript框架
特点:
- 组件化:以组件为基础单位组合成用户界面,方便视图的拆分与模块复用,可以更容易做到高内聚低耦合。
- 声明式:
JSX
方式编写,结构上更容易阅读,而且更容易与其他组件代码进行组合 - 通用性:因为
Virtual DOM
, 使得跨平台成为可能,一处代码到处运行。
缺点:
React
官方没有提供一系列的解决方案,把路由,状态管理等交由社区,导致学习成本和选型方面的造成一点的影响。React
在本身编写过程中组件有些优化的功能需要使用者额外自主处理,比如shouldComponentUpdate
,useMemo
2. React 最新生命周期 ?
React 16 打算废弃的是哪些生命周期
componentWillMount
componentWillReceiveProps
componentWillUpdate
新增的生命周期
static getDerivedStateFromProps
getSnapshotBeforeUpdate
目前 React
的生命周期分为三个阶段,分别是挂载阶段(Mounting)、更新阶段(Updating)、卸载阶段(Unmounting)
挂载阶段:
constructor
: 构造函数,最先被执行,我们通常在构造函数里初始化state对象或者给自定义方法绑定this
getDerivedStateFromProps
:参数为(nextProps, prevState)
,静态方法,无法获取this
, 用来代替componentWillReceiveProps
,返回值作为state
来 定向更新。render
:render
方法不会直接渲染DOM
, 它只是把需要渲染的内容返回回来,真实的渲染DOM
是由ReactDOM.render
操作的componentDidMount
:真实DOM
已经渲染并挂载到页面上后触发,这里可以操作真实DOM
, 异步请求、数据初始化这样的操作也尽量放在这个生命周期来做
更新阶段:
getDerivedStateFromProps
:在父组件状态更新(注意:并不是只有 props改变的情况下,自身状态改变也会触发子组件的此方法)以及自身组件更新的时候都会触发。shouldComponentUpdate
:参数(nextProps, nextState)
,主要用来优化,返回一个布尔值,true
表示会触发重新渲染,false
表示不会触发重新渲染。默认是true
render
:根据上面函数的返回值,是否重新执行getSnapshotBeforeUpdate
:参数(prevProps, prevState)
, 代替componentWillUpdate
,返回值作为componentDidUpdate
的第三个参数,此生命周期必须与componentDidUpdate搭配使用componentDidUpdate
:在组件更新完毕后被触发,经常被用来处理DOM
操作
卸载阶段:
componentWillUnmount
: 当我们的组件被卸载或者销毁了就会调用,我们可以在这个函数里去清除一些定时器,取消网络请求,清理无效的DOM元素等垃圾清理工作
3. 为何尽量避免使用废弃生命周期?
- 比如在
componentWillMount
里发起异步请求,以为可以让页面早点显示出来,但异步请求再怎么快也快不过(React 15
下)同步的生命周期。componentWillMount
结束后,render
会迅速地被触发,所以说首次渲染依然会在数据返回之前执行。这样做不仅没有达到你预想的目的,还会导致服务端渲染场景下的冗余请求等额外问题,得不偿失。 Fiber
架构下会因为中断和重启导致多次调用,如果内部有付款类的接口请求则会出现大问题- 防止各种骚操作,比如在
componentWillReceiveProps
中使用setState
直接爆栈
4. React Hook 比起 Class 有什么优缺点?
- 优点
- 解决
class
的this
和 生命周期两大痛点 - 更好地拆分逻辑代码,每个
Hook
就做一件事 - 更好地复用代码,不会跟
HOC
一样出现嵌套地狱
- 缺点
- 缺少
getSnapshotBeforeUpdate
这类生命周期的实现 - 在过度复杂或者过度拆分方面摇摆不定,对开发者的水平提出了更高的要求。
Hooks
在使用层面有着严格的规则约束(比如不要在循环、条件或嵌套函数中调用Hook
。)
5. React Hook 的工作机制
Hook 数据结构即是一个单向串联的链表
- 首次渲染,执行
mountState
, 创建hook
对象,存储initialState
, 并把hook
加到链路最后方 - 更新,执行
updateState
,按顺序去遍历之前构建好的链表,取出对应的数据信息进
hooks
的渲染是通过 依次遍历
来定位每个 hooks 内容的。如果把Hook 写在 if
语句 里,前后两次读到的链表在顺序上出现差异,那么渲染的结果自然是不可控的。
6. 什么是 Virtual DOM ?
Virtual DOM
就是 JS
和 DOM
之间的一个映射缓存,说白了就是一个描述 DOM
的JS
对象。
React
中是通过 Babel
转译 JSX
之后,调用React.createElement
生成的 JS
对象。
7. 为什么需要 Virtual DOM ?
很多人会说因为直接操作DOM
很慢,会导致频繁的回流与重绘。JS
操作Virtual DOM
不会发生这种情况,所以 Virtual DOM
比 DOM
快...... 其实这并不是正确的结论。
选择Virtual DOM
,可以在每次更新的时候直接对比 Virtual DOM
的差异,然后只需要更新发生变化的部分即可。但这并不是为了防止频繁的操作DOM
带来的不好的性能体验,而是为了让开发者用的爽
,无需去写一堆DOM
操作,直接修改数据,让数据驱动视图,它提供了更高效的研发模式,以及一个还不错的性能体验。
而说 DOM
比 Virtual DOM
快的观点,也是不够严谨的。比如有一串数据非常简单的列表数据, 但又发生了巨大的改变, Virtual DOM
在更新时会经过构建新的虚拟DOM
树 -> diff
比较 -> 渲染真实 DOM
, 这种情况来看,DOM
明显会更快
Virtual DOM
的价值主要体现在提升研发体验/研发效率以及跨平台的功能上
8. React Diff 复杂度是 O (n) ?
在 Diff
过程中,对比两个 Virtual DOM
树的变化,一般找出两个树结构之间的不同,需要循环递归进行树节点的一一对比,这个过程的算法复杂度是 O (n^3)
而 React
团队通过分层对比
,即只针对相同层级的节点作对比,如果如果节点是同一类型才会向下比较,否则放弃比较,直接原地替换掉旧的节点。这样的时间复杂度是 O (n)
9. React key 的作用? 可不可以使用 index 作为 key ?
key
主要解决的是同一层级的下节点
的重用问题。比如对比两个列表数据的变化,key
为每个节点添加了唯一
标识,而在 Diff
通过这些标识来判断节点是否销毁,移动,新增。
如果不写 key
或者使用 index
作为 key
, 则标识无法判断唯一性
,也就不知道节点是否移动或者销毁,这就会导致在替换过程中直接复用当前节点的内容。这就可能出现节点状态
在此过程中出现错误的情况,比如input
输入框内数据在更新后还会存在于原来位置的问题。
10. setState 到底是同步的,还是异步的?
在通常情况下 setState
是异步的,但在比如 setTimeout
里却是同步的。
- 异步
state = {
count: 0;
}
this.setState({
count: this.state.count + 1
});
this.setState({
count: this.state.count + 2
});
this.setState({
count: this.state.count + 3
});
consle.log(this.state.count); // 结果为 3
打印结果依旧为 0
, 而且更新之后,count
会变成 3
,而不是1 + 2 + 3 = 6
,因为在多次调用setState
后,count
并不会累次相加,而是会加入一个任务队列进行合并操作,对于相同属性的设置只会执行最后一次的更新
- 同步
state = {
count: 0;
}
setTimeout(() => {
this.setState({
count: this.state.count + 1
});
consle.log(this.state.count); // 结果为 1
}, 0)
由于 setTimeout
的宏任务机制,逃出了 React
的异步掌控,在执行的时候, React
内部已经关闭了批量更新的标识.
11. Stack Reconciler vs Fiber Reconciler
- Stack Reconciler
React 15
的 Reconciler
机制,由于 JavaScript线程
和 渲染UI线程
互斥,所以如果 React
组件足够复杂,在更新之时会同步递归渲染整个组件树导致 JavaScript线程
一直占用 主线程
, 而 渲染UI线程
则长时间挂起,就会出现卡顿
的效果。
- Fiber Reconciler
Fiber
则把一个庞大的更新任务被分解为了一个个的工作单元,每个工作单元有着不同的优先级,通过 Scheduler
来调度这些优先级,如果更新过程中有更高的优先级任务(比如用户输入),则中断当前任务,待渲染完成后继续执行之前的任务(所以存在部分生命周期重复执行的问题)
结语
其实很多内容可以结合源码来看,每个点都可以写很长的一篇文章,这里种子做了一个简单的整理,并不能作为面试的回答,因为确实比较浅显。更多的作用是作为学习和复习的引子,先作为理论,之后结合源码深入学习。
参考文献
- 深入浅出搞定 React
- 前端面试宝典之 React 篇
- 2019年17道高频React面试题及详解
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!