React面试类型思维导图
react面试题类型进行梳理汇总
Reacr基本概念
1. 生命周期
1.1. react有哪些生命周期?
答:16.3版本的
在react中有三个阶段,分别是初始化阶段、重渲染阶段和销毁阶段
在初始化阶段下有4个生命周期
(1)constructor
(2)getDrivedStateFromProps
(3)render
(4)componentDidMount
在重渲染阶段有5个生命周期
(1)getDrivedStateFromProps
(2)shouldComponentUpdate
(3)render
(3)getSnapShotBeforeUpdate
(4)componenDidUpdate
在销毁阶段有一个生命周期
(1)componentWillUnmount
1.2. 为什么出现getDrivedStateFromProps这个生命周期来代替之前的生命周期钩子?
答:getDrivedStateFromProps这个生命周期取代了之前的willMount、willUpdate、willPreceveProps这三个生命周期。主要是因为之前的生命周期钩子会出现使用逻辑混乱的问题。
并且在之前willPreciveProps这个钩子在props发生变化的时候会触发,但是如果父组件重新渲染,那么即使props数据没有变化,也会触发钩子。让开发者在使用时会判断困难。
在被取代的这些生命周期中,由于是在render之前触发的,所以还没有实例,也就无法获取到组件的state和props的数据。在getDrivedStateFromProps中可以通过第一个参数和第二个参数获取到state和props里的值。
1.3. getSnapShotBeforeUpdate这个生命周期是用来做什么的?
答:这个是用于保存更新前的节点快照的钩子,可以在最后渲染之前把节点的属性保存下来。比如在滚动组件中可以保存滚动之前的位置信息,然后页面ui渲染以后,再根据这个位置信息进行视觉上的恢复。
2. 数据通讯
2.1. react中的组件有哪些数据通讯的方式?
答:在react中数据通讯有一下几个场景:
(1)父传子:通过props
(2)子传父:通过回掉函数,或者通过ref绑定子组件
(3)兄弟通讯:利用父组件作为中间件的方法,也就是状态提升
(4)父向下面的所有子孙组件通讯:利用context
(5)非关系组件通讯:使用全局状态管理工具redux
2.2. 详细讲一下怎么通过ref的方式获取子组件的属性和方法?
答:
(1)首先在父组件中通过useRef创建一个ref。
(2)然后在子组件里使用forwardRef包裹自身,再接收从父组件那传送过来的ref,
(3)在子组件中引入一个钩子函数useImperativeHandle,它第一个参数是接受一个ref,第二个参数是执行一个回掉函数,函数里返回一个带有自身属性或方法的对象。
(4)在父组件中就可以通过ref.current来获取子组件暴漏出的属性和方法了
2.3. 讲一下Context的使用?
答:Context是提供被包裹组件中所有子组件的数据共享的能力。
首先是定义数据:
通过creatContext定义一个context来存放数据
然后通过这个context.provider 标签来包裹需要共享数据的根组件
然后通过value这个属性把共享数据向下传递
子组件消费数据:
使用useContext这个钩子函数,参数为刚刚定义的context。就可以获取到context.provider里的value中向下传递的值了。
3. setState
3.1. setState的同步异步问题和合并的问题
答:在react18版本以前,setState在不同情况下分为同步和异步执行的情况。在react合成事件中setState是异步执行的。在setTimeout和DOM事件中,setState是同步执行的的。当setState是异步的时候,就会发生state合并的问题。可以在第一个参数中传入一个函数来解决state的合并问题。当setState是异步的时候就无法获取到最新的值,可以在setState的第二个参数中执行一个回掉函数中获取最新的值。
在react18版本中,做了一个优化,引入了Automatic Batching(自动批处理机制),不会对不同情形下的setState的同步和异步做区分。而是全部变成了异步更新 + 合并state。
3.2. 什么是batchUpdate机制?
答:batchUpdate机制就是合并更新。在每次执行 useState 的时候,组件都要重新 render 一次,会造成无效渲染,浪费时间(因为最后一次渲染会覆盖掉前面所有的渲染效果)。 所以 react 会把一些可以一起更新的 useState/setState 放在一起,进行合并更新。
3.3. 什么是transaction事务机制?
答:Transaction会包装一个方法 func。包装的过程就是将提供的wrapper里的initialize方法放在func之前,close方法都放在func之后。这样func执行的时候,前后就植入了一些代码做一些初始化或者是清理的工作。
为什么这样做?
4. hooks
4.1. react为什么推出了hooks?
答:在react16.8版本的时候推出了函数组件,在react官方文档中认为view=fn(props),即每个组件都可以看成是一个函数。函数组件便于对复杂逻辑进行拆分、提取公共组件这些优点。但是纯函数组件不能存储state数据,没有生命周期等这些问题。无法满足更复杂的业务需求。所以hooks的出现就是赋予函数组件更强大能力而推出的。
4.2. react的常见hooks有哪些?
答:
useState
useEffect
useLayoutEffect
useCallback
useMemo
useRef
useContext
useReducer
4.3. useEffect都分别模拟了哪些生命周期?
答:useEffect可以根据四种情形来模拟不同的生命周期。
第一种是useEffect没有第二个参数,则在第一次加载组件和每次数据发生变化就会触发调用,模拟的是DIdMout + DidUpdate
第二种是useEffect第二个参数是空数组,则只在第一次加载组件的时候触发调用,模拟的是DIdMount
第三种是useEffect第二个参数是一个数组,且里面有监听的变量。则在第一次加载组件,且监听的变量的值发生变化的时候调用。模拟的是DidMount + DIdUpdate
第四种是useEffect里第一个参数里return一个函数,则在下次effect执行之前调用这个return出来的函数,模拟的是willUnMount
4.4. useLayoutEffect和useEffect的区别?
答:useLayoutEffect是同步执行的,会在组件挂载到DOM之后,浏览器渲染之前执行。因此会阻塞浏览器的渲染,
useEffect是异步执行的,在浏览器渲染内容之后执行。不会阻塞浏览器的渲染。如果需要在渲染之前执行副作用操作,例如获取DOM元素的尺寸,则可以使用useLayoutEffect。
4.5. 为什么hook要以use开头?
答:如果不以“use” 开头的话,无法判断这个函数是否包含对其内部Hook 的调用,React 将无法自动检查这个自定义Hook 是否违反了 Hook 的规则
4.6. hooks的使用规范
答:
(1)hook只能在函数式组件或者自定义hook中使用。
(2)hook必须在组件最顶层使用;hook不能在判断语句或者循环语句中使用。
4.7. 为什么Hooks不能在判断语句或者循环语句中使用
答:每个hook的调用都会有对应的状态被保存,并且与组件实例关联。在函数组件中,每次渲染都是全新的实例,因此hooks要保证在每次渲染结束后要以相同的顺序被调用,否则会出现保存的状态被取错的问题。但是在if语句或者循环中使用hook,可能会导致hooks执行顺序不一致,所以不可以在其中使用。
4.8. useState 是异步的吗?那它是属于宏任务或者微任务的一种吗?
答:useState本身是同步的,但是React在更新中可能进行批更新,以此来减少更新次数,提高性能。所以useState看起来像是异步的。
它不属于宏任务或者微任务,但是react的更新通常被认为是宏任务。
4.9. useState的实现原理?
答:useState里的数据是通过闭包来实现数据的保存和修改的,useState分为mountState(初次渲染阶段)和updateState(组件更新阶段)两个阶段。在mountState阶段,会
4.10. useEffect的实现原理?
4.11. 你自定义了哪些hook,自定义hook应该注意什么
定义过usePermiss这个判断权限的Hooks,在这个里面传入权限状态码key和权限组件,在hooks里面调用权限接口,如果有权限就返回权限组件,如果无权限就返回一个无权限的固定页面。
注意事项:自定义hook要以use开头;每个自定义hook应该是独立和封闭的,不依赖外面的状态。
5. 虚拟dom(Virtual DOM)
5.1. 什么是虚拟dom?
答:虚拟DOM其实就是一个可以对应真实DOM的一个js对象,通常包含标签名、标签上的属性、事件监听和子元素。
5.2. 虚拟dom有哪些优点和缺点?
答:
优点:
(1)优化性能,频繁的操作DOM比较消耗性能,但是可以频繁操作虚拟DOM,然后把最终的结果转成真实DOM
(2)可以跨平台渲染,虚拟DOM不仅可以变成DOM,还可以变成小程序,app应用。
缺点:
缺点是需要额外的创建函数creatElement来创建虚拟dom。如果使用jsx这个语法糖通过Babel来转译成creatElement,则这个过程严重依赖打包工具。
6. JSX
6.1. jsx的本质是什么?
答:JSX的本质是一个语法糖,利用bable把jsx语法转化成合法的js语法。也就是转化成React.creatElement()来生成构建节点的。
6.2. 为什么jsx里返回的根节点组件只能有一个?为什么自定义组件首字母必须是大写?
答:React.creatElement()这个方法,接收的第一个参数是节点的标签名,第二个参数是节点里的属性。第三个参数是节点中的文本。由于jsx是一个语法糖,最终会被babel转换成React.creatElement()。所以只能返回一个根节点组件,对应到第一个参数里。
而creatElement会根据标签名的首字母大小写来区分是原生标签还是用户自定义标签,所以自定义组件的首字母必须是大写
6.3. JSX转换成真实DOM的过程
答:使用React.createElement或者JSX编写React,JSX代码最后会被babel转换成createElement。createElement函数会构造出一个虚拟DOM对象,然后进行新旧虚拟DOM对比,也就是diff算法。ReactDOM.render将两者的差异patch更新到实际DOM中。
扩展:这个pacth的过程分为两个阶段,第一个是reconciletion阶段和commit阶段。在fibber中有讲到这两个阶段
7. diff
7.1. 什么是diff算法?
答:对比新旧虚拟DOM之间的差异,根据差异更新到真实DOM的过程,这个对比时使用的算法就是diff算法。
7.2. 讲一下react的diff算法
答:react的diff算法遵循三个层级的策略
(1)只比较同一层级,不跨级比较
(2)tag不相同,则直接删除重建,不再深层比较
(3)tag和key,两者都相同,则认为是同一个节点,不再深度比较
7.3. 遍历数据渲染组件的时候,为什么要加上唯一值key?
答:通过key可以准确的发现节点是否一样,如果是相同节点则不需要删除再新增,这就需要保证key是唯一值。
8. 合成事件机制
8.1. 什么是react的合成事件机制?
答:在react中event并不是原生event,而是react模拟了一个和原生事件具有相同能力的模拟事件。并且可以通过event.nativeEvent属性来访问原生DOM事件。react把这个合成事件统一挂载到了document上。在react17版本以后,是挂载到了root上。
8.2. react为什么要合成事件机制?
答:(1)为了减少内存消耗,避免频繁绑定和解绑。
(2)方便事件统一管理,只需维护在document上的一个事件映射。
(3)更好的兼容性和跨平台,当需要跨端的时候,只需要更改原生事件和合成事件的内部交互逻辑即可。
8.3. react在17版本以后为什么开始绑定到了root组件上了?
答:为了多了React版本共存,例如微前端
9. React18新特性
9.1. React18新增了哪些新特性?
答:
(1)新增了createRoot这个api,可以使用这个api代替之前的ReactDom.render的挂载方式,这样react创建的根组件就挂载到了root这个节点上。
(2)新增了Automatic Batching(自动批处理机制)。
(3)新增了concurrency(并发渲染)的概念
(4)新增了Suspense这个方法来搭配懒加载去使用,里面的callback这个属性可以定义成loading的样式组件
9.2. 什么是Automatic Batching机制?
答:当连续两个state发生变更的时候,放在一起执行处理,这样页面只更新渲染一次。在18之前也有类似的机制,不过是只在react合成事件中有效,在setTimeout,promise,自定义事件中无效。现在在18版本中,可以全面生效。
9.3. 什么是concurrency并发渲染?
答:concurrency是一种理念,它把用户的行为分为低优先级和高优先级,这样就会有更多的cpu资源来优先渲染高优先级的任务。可以通过useTransition这个hook对执行的函数进行降级处理,或者通过useDeferredValue把数据带入到慢速渲染通道中。
9.4. useTranstion和useDeferredValue的区别?
答:useTranstion是处理一个执行的函数或多个执行的函数,使之降级。useDeferredValue是把数据进入到慢速渲染通道中,只对单个数据进行处理。
9.5. Concurrency模式实现原理?
10. 对比vue
10.1. react和vue相比有哪些区别?
答:
(1)数据流不同,vue是通过v-module进行双向数据绑定,react是单向数据流
(2)数据监听响应方式不同。
vue会遍历data对象,通过object.definedPropert(),使每个属性转化为setter和getter方法。数据发生改变的时候,会触发setter方法,并通知所有依赖这个的数据的watcher实例调用updata方法来更新渲染。
react则是通过setState来更新状态,从而触发渲染
(3)diff算法不同,vue采用的是首尾指针法,react采用的是从左到右进行比较的方法
(4)模版渲染方式不同:在vue中是通过一些指令如v-if、v-show这些指令来渲染模版。而在react中,是通过jsx这种类似原生js的方式来渲染的。
10.2. react、vue2.0、vue3.0的diff算法有哪些区别?
React高级概念
1. Filber
1.1. 讲一下对Filber的理解,Filber解决了什么问题?
答:React的挂载和更新会经历reconciliation阶段和commit阶段。在reconciliation阶段中,会使用diff算法对虚拟DOM这个对象进行一个深度遍历。计算出差异然后更新UI。由于js是单线程,且和DOM渲染共用一个线程,如果这个虚拟DOM很庞大,那么整个js的计算所消耗的时间会很长。大量的同步js计算就会阻塞UI渲染。这样会使得用户感觉页面有掉帧的现象。
为了解决这个问题,react团体在react16.8的时候引入了filber这个架构,来拆分reconcilation这个阶段,把一个大的虚拟DOM拆分成多个小的fiber节点。这样的话计算任务也被拆分成多个小的计算任务。当UI需要渲染的时候暂停计算任务,空闲时恢复,主要是利用了window.requestIdelCallback这个API实现的。
1.2. Filber是怎么实现任务(task)中断可续连的?
答:Filber就是根据虚拟DOM树生成多个fibber节点,并构成一个链表数据结构,它里面包含三个节点指针,第一个是child(父节点指向第一个字节点的指针),第二个是sibling(指向下一个兄弟元素的指针),第三个是return(指向父元素的指针)。这样当任务发生中断的时候,只需要保留当前节点的索引,断点就可以恢复,因为每个节点都保留着其对父节点的索引。
1.3. Filber是怎么把大的task拆分成多个小task的?
答:通过把一个虚拟DOM生成一个个小的节点,也就是Filber节点。这些Filber节点以链表的形式组合起来。节点里面包含了执行渲染所需的信息,这样就把大的task任务拆分成了多个小的task任务。
1.4. Filber里的空闲时间是怎么计算的?
答:基于浏览器的 requestIdleCallback 和 requestAnimationFrame 这个两个api来计算当前的空闲时间,requestIdleCallback这个api会在浏览器空闲的时间发生回掉。而requestAnimationFrame里可以传递一个回调函数,在浏览器下一次重绘的时候执行,这个回掉函数中可以计算当前剩余时间。
1.5. 在commit这个阶段中都做了什么事情?
答:commit 阶段是将reconciliation阶段计算得出的变更应用到实际的 DOM 上,并执行相应的操作,包括执行 DOM 更新、生命周期方法、处理 Ref 回调以及执行其他副作用等(简单回答,下面的答案太难背了)
commit阶段主要任务就是把在协调阶段中计算的差异到实际的DOM中。commit可以分为三个阶段,Before Maution、Maution、Layout。
在BeforeMaution这个阶段,主要就是类组件调用了getSnapShotBeforeUpdate这个生命周期生成快照对象保存起来。
在Maution这个阶段,主要就是操作DOM
在Layout这个阶段,去执行一些生命周期或者副作用
2. 性能优化
2.1. 在项目中都做了哪些性能优化?
答:
(1)SCU优化
(2)使用异步组件
(3)webpack层面的优化,比如开启多线程;压缩源文件等
(4)图片懒加载
(5)使用SSR
2.2. 什么是SCU优化?
答:react默认的是如果父组件有更新,则子组件无条件也进行更新。SCU优化就是在shouldComponentUpdate这个生命周期中的优化。SCU中默认是返回true,代表了可以渲染。可以在这个钩子里进行数据比较处理,如果state或者props中的数据没有发生变化则返回false,组件可以不进行重新渲染。以此达到优化的目的。
2.3. 什么是PureComponent和meno?
答:PureComponent和memo都是react官方提供的SCU优化的api,它们都会对state和props进行浅比较,区别是一个应用到类组件,一个应用到函数组件。
2.4. 异步组件有什么用?怎么用?
答:当组件比较大的时候或者路由需要懒加载的时候,可以使用异步组件来提高它的性能。
用法:使用React.lazy 这个函数,它的参数是通过import来导入一个组件,然后就得到了一个异步组件,在异步组件一般搭配React.Suspense使用。在异步加载组件过程会触发Suspense里的callbalk属性定义内容,比如展示loading动画。
3. 错误捕获
3.1. 讲一下对错误捕获的理解?
组件级别的错误捕获:
React 16 引入了错误边界的概念,在componentDidCatch里可以捕获错误信息。我们可以利用这个特点,封装一个组件,如果页面报错,组件里通过componentDidCatch来捕获错误信息,并展示一个定义好的错误页面,否则的话就展示props.children这个页面。
js代码级别的错误捕获
除了使用错误边界来捕获特定组件的错误外,可以window.onerror 来捕获全局的js的错误
4. Portals(传送门)
4.1. 什么是Portals?
答:Portals是一种把组件挂载到父组件以外节点的能力。
具体使用是:ReactDom.creatPortal(组件,挂载节点)
4.2. Portals的使用场景?
答:
(1)父组件设置了overflow:hidden;
(2)父组件的z-index太小,想逃离父组件
React 全家桶
1. Redux
1.1. Redux的基本概念以及工作流程是什么?
答:Redux是为了解决全局状态需要集中管理的库,当一处地方更改了全局状态,其它关联此数据的组件都需要做出响应。它有三大原则,分别是不可变值、state是只读的、只能使用纯函数来更改。
**
Redux的工作流程是首先通过creatStore创建了一个公共的状态管理空间store,creatStore里面接受一个reducer这么一个纯函数来管理这个store。当用户执行某一操作想修改store里的值的时候,可以调用store.dispatch(action)来触发更新操作,然后进入到reducer中,会根据action的type和其它的属性来返回一个新的state,然后用户通过store.subscribe就可以监听到state发生了数据的更改,并通过store.getState()获取到最新的state的值。
1.2. 描述一下redux的单向数据流
action触发dispatch到reducer,reducer根据action返回一个新的state,state变化后会出触发监听state变化的函数,然后响应到视图中
1.3. redux和react-redux有什么区别?
react-redux是方便redux在react项目中更好的使用而设计出来的。但是redux和react-redux这两个插件都需要安装才可以。
在react-redux中其实是利用了context这个通讯方式的原理,通过Provuder把组件包裹起来,然后把创建的store向下传递下去。react-redux有两种方法来更改state和监听state,第一种是用hook的方式,第二种是使用connect为组件赋能的方式。
hook的方法: react-redux有两个hook,useSelector来监听state的变化,useDispatch来更改state的变化。
connect的方法: 可以引用connect这个api对子组件进行包裹,connect里第一个参数是mapStateToProps这个函数,里面可以把state的值通过props传递给子组件。第二个参数是mapDispatchToProps这个函数,里面可以定义修改store的方法。
1.4. redux的中间件有哪些?
答:中间件有:redux-thunk、redux-saga、redux-promise、redux-logeer,在创建store的时候通过applyMiddleware来集成中间件。
1.5. redux如何处理异步action?
答:使用redux-thunk、redux-saga、redux-promise
1.6. redux的中间件的工作原理是什么?
答:redux的中间件就是对dispatch这个过程来插入一些逻辑和方法,这样就可以改造dispatch,为它赋能。
1.7. redux-thunk的原理是什么?
答:在redux-thunk里会判断当前action是否是函数,如果是函数就调用它,否则的话就进行下一个中间件。
1.8. 市面上还有哪些类似于redux的全局状态管理工具,它们各自的区别是什么?
答:Rematch、Jotai、Hookstate
1.9. 一个状态管理工具应该具有哪些能力?
2. React-Router
2.1. react-router如何传递参数?
2.2. React-Router的路由模式有哪些?
答:有hash路由和history路由,分别对应HashRouter组件和BrowserRouter组件。
2.3. HashRouter模式和BrowserRouter模式的区别?
答:
(1)底层原理不一样,BrowserRouter 调用的是h5 history API,HashRouter是使用的url的哈希值
(2)地址表现形式不一样,HashRouter会在端口号后添加/#/
(3)刷新后对路由state参数的影响不一样,BrowserRouter 刷新后没有任何影响,因为state保存在history对象中。HashRouter会导致state参数的丢失。