React前端面试题(未完成版)

1.如何避免生命周期的坑

在这里插入图片描述
那么下面7种情况最容易造成生命周期的坑:

getDerivedStateFromProps 容易编写反模式代码,使受控组件和非受控组件区分模糊
componentWillMount 在 React 中已被标记弃用,不推荐使用,主要的原因是因为新的异步架构会导致它被多次调用,所以网络请求以及事件绑定应该放到 componentDidMount 中
componentWillReceiveProps 同样也被标记弃用,被 getDerivedStateFromProps 所取代,主要原因是性能问题。
shouldComponentUpdate 通过返回 true 或者 false 来确定是否需要触发新的渲染。主要用于性能优化。
componentWillUpdate 同样是由于新的异步渲染机制,而被标记废弃,不推荐使用,原先的逻辑可结合 getSnapshotBeforeUpdate 与 componentDidUpdate 改造使用。
如果在 componentWillUnmount 函数中忘记解除事件绑定,取消定时器等清理操作,容易引发 bug。
如果没有添加错误边界处理,当渲染发生异常时,用户将会看到一个无法操作的白屏,所以一定要添加。

React 的请求应该放到哪里?为什么?
对于异步请求,应该放到componentDidMount 中去操作,从生命周期函数执行顺序来看,除了componentDidMount 之外还有下面两种选择:
constructor 中可以放,但是不推荐,因为 constructor 主要用于初始化 state 和函数绑定,并不承载业务逻辑,而且随着类属性的流行,constructor 很少用啦
componentWillMount 已被标记废弃,因为在新的异步渲染下该方法会触发多次渲染,容易引发bug,不利于 React 的后期维护和更新
所以React 的请求放在 componentDidMount 里是最好的选择。

2.说说Real diff算法是怎么运作的?

diff算法的作用
在DOM需要更新的时候,通过diff算法可以 计算出 虚拟DOM 中真正变化的部分,从而只针对变化的部分进行更新渲染,避免”牵一发而动全身“,造成性能浪费。

普通的diff算法
虽然完美地实现了找不同的功能,但是傻瓜式的循环递归对节点进行依次的对比,使其算法的时间复杂度为O(n^3),其中n是dom树的节点数。如果dom数足够大的话,这个算法将对cpu形成绝杀。

React中的diff算法
为了优化diff算法,react中对普通的diff算法实行了三大策略,成功将时间复杂度降为O(n)

策略一:tree diff —— 层级对比

由于开发过程中极少出现DOM的跨层级移动,所以tree diff 忽略了DOM节点的跨层级移动。(react不建议开发人员跨层级移动DOM)

component diff —— 组件对比

同类型的两个组件,直接比较Virtual DOM树,不同类型的组件将会被判定作为脏组件(dirty component)处理,直接删除或创建新组件

策略三:element diff —— 节点对比

对于同一层级的一组子节点,通过分配唯一唯一key值进行区分

3.调和阶段setState干了什么?

当调用setState的时候,react做的第一件事就是将传递给setState的对象全部都整合到当前组件的状态当中。这会启动一个“和解”的一个过程,和解过程的目的就是能够以更加有效的方式,会根据新的状态来更新ui。为了弄清新的ui如何响应新的状态而改变,react会将一个新的树和上一个树作比较。

4.说说redux的实现原理是什么,写出核心代码?

二级目录为什么要使用redux?

随着互联网的高速发展,我们的应用变得越来越复杂,进行导致我们的组件之间的关系也变得日趋复杂,往往需要将状态父组件 -> 子组件 -> 子子组件 -> 又或者只是简单的 父组件与子组件之间的props传递也会导致我们的数据流变得难以维护,因为二次开发者不在熟悉项目的情况下无法第一时间确定数据来源是由谁发起的。使用redux之后,所有的状态都来自于store中的state,并且store通过react-redux中的Provider组件可以传递到Provider组件下的所有组件,也就是说store中的state对于Provider下的组件来说就是全局的。

redux的核心原理是什么?

1.将应用的状态统一放到state中,由store来管理state。
2.reducer的作用是返回一个新的state去更新store中对用的state。
3.按redux的原则,UI层每一次状态的改变都应通过action去触发,action传入对应的reducer 中,reducer返回一个新的state更新store中存放的state,这样就完成了一次状态的更新
4.subscribe是为store订阅监听函数,这些订阅后的监听函数是在每一次dipatch发起后依次执行
5.可以添加中间件对提交的dispatch进行重写

5.React合成事件的原理?

react 合成事件机制采用了事件代理的思想,将每个 react 事件依赖的原生事件绑定到了 root 元素上。事件处理函数不是绑定到组件的元素上的,而是绑定到root上,这和fiber树的结构特点有关,即事件处理函数只能作为fiber的prop。
绑定到root上的事件监听不是我们在组件里写的事件处理函数,而是一个持有事件优先级,并能传递事件执行阶段标志的监听器。

6.React组件之间如何通信?

父组件向子组件传递
由于React的数据流动为单向的,父组件向子组件传递是最常见的方式
父组件在调用子组件的时候,只需要在子组件标签内传递参数,子组件通过props属性就能接收父组件传递过来的参数
子组件向父组件传递
子组件向父组件通信的基本思路是,父组件向子组件传一个函数,然后通过这个函数的回调,拿到子组件传过来的值
兄弟组件之间的通信
如果是兄弟组件之间的传递,则父组件作为中间层来实现数据的互通,通过使用父组件传递
非关系组件传递
如果组件之间关系类型比较复杂的情况,建议将数据进行一个全局资源管理,从而实现通信,例如redux。关于redux的使用后续再详细介绍

7.为什么react元素有一个$$type属性?

目的是为了防止 XSS 攻击。 因为 Synbol 无法被序列化,所以 React 可以通过有没有
t y p e o f 属 性 来 断 出 当 前 的 e l e m e n t 对 象 是 从 数 据 库 来 的 还 是 自 己 生 成 的 。 如 果 没 有 typeof 属性来断出当前的 element 对象是从数据库来的还是自己生成的。 如果没有
typeof属性来断出当前的element对象是从数据库来的还是自己生成的。如果没有
typeof 这个属性,react 会拒绝处理该元素。

8.说说Connect组件的原理是什么?

Connect原理就是在入口文件当中使用Provider将整个组件包裹起来,都成为他的子组件,接受redux中的store作为props,然后通过context传递给子孙组件上的connect

作用:连接React组件与Redux Store
mapStateToProps允许我们将 store 中的数据作为 props 绑定到组件上
mapDispatchToProps将action作为props绑定到MyComp上。
mergeProps不管是stateProps还是dispatchProps,都需要和ownProps merge 之后才会被赋给MyComp。connect的第三个参数就是用来做这件事。通常情况下,你可以不传这个参数,connect就会使用Object.assign替代该方法。
 
首先connect之所以会成功,是因为Provider组件:
 
在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
接收Redux的store作为props,通过context对象传递给子孙组件上的connect
那connect做了些什么呢?
 
它真正连接 Redux 和 React,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
 
关于它的源码
 
connect是一个高阶函数,首先传入mapStateToProps、mapDispatchToProps,然后返回一个生产Component的函数(wrapWithConnect),然后再将真正的Component作为参数传入wrapWithConnect,这样就生产出一个经过包裹的Connect组件,该组件具有如下特点:
 
通过props.store获取祖先Component的store
props包括stateProps、dispatchProps、parentProps,合并在一起得到nextState,作为props传给真正的Component
componentDidMount时,添加事件this.store.subscribe(this.handleChange),实现页面交互
shouldComponentUpdate时判断是否有避免进行渲染,提升页面性能,并得到nextState
componentWillUnmount时移除注册的事件this.handleChange
 
 
 
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {
     
     }) {
    
    
  return function wrapWithConnect(WrappedComponent) {
    
    
    class Connect extends Component {
    
    
      constructor(props, context) {
    
    
        // 从祖先Component处获得store
        this.store = props.store || context.store
        this.stateProps = computeStateProps(this.store, props)
        this.dispatchProps = computeDispatchProps(this.store, props)
        this.state = {
    
     storeState: null }
        // 对stateProps、dispatchProps、parentProps进行合并
        this.updateState()
      }
      shouldComponentUpdate(nextProps, nextState) {
    
    
        // 进行判断,当数据发生改变时,Component重新渲染
        if (propsChanged || mapStateProducedChange || dispatchPropsChanged) {
    
    
          this.updateState(nextProps)
            return true
          }
        }
        componentDidMount() {
    
    
          // 改变Component的state
          this.store.subscribe(() = {
    
    
            this.setState({
    
    
              storeState: this.store.getState()
            })
          })
        }
        render() {
    
    
          // 生成包裹组件Connect
          return (
            <WrappedComponent {
    
    ...this.nextState} />
          )
        }
      }
      Connect.contextTypes = {
    
    
        store: storeShape
      }
      return Connect;
    }

9.说说你对fiber架构的理解?解决了什么问题?

是什么

React Fiber 是 Facebook 花费两年余时间对 React 做出的一个重大改变与优化,是对 React 核心算法的一次重新实现。从Facebook在 React Conf 2017 会议上确认,React Fiber 在React 16 版本发布
在react中,主要做了以下的操作:
为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新,注意是重新执行优先级低的任务
增加了异步任务,调用requestIdleCallback api,浏览器空闲的时候执行
dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行

从架构角度来看,Fiber 是对 React核心算法(即调和过程)的重写
从编码角度来看,Fiber是 React内部所定义的一种数据结构,它是 Fiber树结构的节点单位,也就是 React 16 新架构下的虚拟DOM

如何解决

Fiber把渲染更新过程拆分成多个子任务,每次只做一小部分,做完看是否还有剩余时间,如果有继续下一个任务;如果没有,挂起当前任务,将时间控制权交给主线程,等主线程不忙的时候在继续执行

即可以中断与恢复,恢复后也可以复用之前的中间状态,并给不同的任务赋予不同的优先级,其中每个任务更新单元为 React Element 对应的 Fiber节点

实现的上述方式的是requestIdleCallback方法

window.requestIdleCallback()方法将在浏览器的空闲时段内调用的函数排队。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应

首先 React 中任务切割为多个步骤,分批完成。在完成一部分任务之后,将控制权交回给浏览器,让浏览器有时间再进行页面的渲染。等浏览器忙完之后有剩余时间,再继续之前 React 未完成的任务,是一种合作式调度。

该实现过程是基于 Fiber节点实现,作为静态的数据结构来说,每个 Fiber 节点对应一个 React element,保存了该组件的类型(函数组件/类组件/原生组件等等)、对应的 DOM 节点等信息。

10.说说你对redux中间件的理解?常用的中间件有哪些?实现原理?

是什么

中间件(Middleware)在计算机中,是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的
在这篇文章中,了解到了Redux整个工作流程,当action发出之后,reducer立即算出state,整个过程是一个同步的操作
那么如果需要支持异步操作,或者支持错误处理、日志监控,这个过程就可以用上中间件

常用的中间件

redux-thunk:用于异步操作
redux-logger:用于日志记录

11.React性能优化的手段有哪些?

React通过虚拟dom等方法是很稳定的,但是在一些某些特定的情况下,我们还是要进行一个性能优化的,而性能优化的手段都有哪些呢,第一个服务端渲染,第二个懒加载组件,第三个react使用组件库避免随意标记,第四个就是事件绑定。

12.说说你对事件循环event loop的理解?

首先,JavaScript是一门单线程的语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环

在JavaScript中,所有的任务都可以分为

同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行

异步任务:异步执行的任务,比如ajax网络请求,setTimeout定时函数等

13.前端跨域的解决方案?

JSONP跨域

jsonp的原理就是利用

跨域资源共享(CORS)

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
浏览器将CORS跨域请求分为简单请求和非简单请求

14.数组常用方法及作用,至少15个?

join() (数组转字符串)
push():方法可向数组的末尾添加一个或多个元素,并返回新的长度。
pop():方法用于删除并返回数组的最后一个元素。
shift():方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
unshift():方法可向数组的开头添加一个或更多元素,并返回新的长度。
sort()(排序)
reverse() (反转数组)
concat() (连接两个或多个数组)
slice()(数组截取)
splice()
indexOf()--------array.indexOf(item,start) (从数组的开头(位置 0)开始向后查找)
item: 必须。查找的元素。
start:可选的整数参数。规定在数组中开始检索的位置。如省略该参数,则将从array[0]开始检索。
lastIndexOf()--------array.lastIndexOf(item,start) (从数组的末尾开始向前查找)
item: 必须。查找的元素。
start可选的整数参数。规定在数组中开始检索的位置。如省略该参数,则将从 array[array.length-1]开始检索
forEach():对数组进行遍历循环,这个方法没有返回值。jquery()也提供了相应的方法each()方法。
map():指“映射”,方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

15.React render方法的原理,在什么时候会触发?

React render方法在类组件和函数组件当中的表示方法是不一样的。
在类组件当中:render方法指的就是render方法;
在函数组件当中:render方法指的是函数组件本身;
在render方法当中,react将新调用的render返回的新的dom和旧的dom进行一个比较,这是更新dom的必要步骤,然后进行diff计算,更新dom树。
在类组件当中,当setState方法进行调用进行数据刷新的时候,render函数就会被触发。
在函数组件当中,当useState hook被使用的时候,数据就会被更新。

16.说说你对vue中mixin的理解?

mixin(混入),提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。
本质其实就是一个js对象,它可以包含我们组件中任意功能选项,如data、components、methods、created、computed等等
我们只要将共用的功能以对象的方式传入 mixins选项中,当组件使用 mixins对象时所有mixins对象的选项都将被混入该组件本身的选项中来
在Vue中我们可以局部混入跟全局混入
优先递归处理 mixins
先遍历合并parent 中的key,调用mergeField方法进行合并,然后保存在变量options
再遍历 child,合并补上 parent 中没有的key,调用mergeField方法进行合并,保存在变量options
通过 mergeField 函数进行了合并

17.for…in循环和for…of循环的区别?

for of 特点 for of 循环用来获取一对键值对中的值,而 for in 获取的是 键名 一个数据结构只要部署了 Symbol.iterator 属性, 就被视为具有 iterator接口, 就可以使用 for of循环。

18.Js数据类型判断都有哪几种方式?至少说出5种?它们的区别是什么?

第一种是:typeof方法,他返回的是字符串类型,
第二instanceof方法,表达方法事 A instanceof B
第三 constanceof,是js原型对象上的属性,
第四,object,protopype,toString,可以去检查对象类型
第五,Array,isArray()去判断是否数据组类型

19.说说你对Object.defineProperty()的理解?

Object.defineProperty ()允许精准添加或者修改对象上的一个属性。 通过const obj = {};obj.foo = 1这种赋值方式增添的属性,可以通过for…in或者Object.keys枚举,他的值可能发生改变,也可能被删除。

20.说说你对webSocket的理解?

WebSocket的理解:webSocket是html5提供的服务器和浏览器全双工通信的网络通信技术。他属于应用层协议,他基于TCP协议并复用了http的握手通道。浏览器和服务器只需要完成一次握手,就可以完成永久性的连接,并可以进行双向通信。
WebSocket的原理:服务器向webSocket通知一个带有所有接收者ID的事件,服务器接收到之后通知所有活跃的客户端,只有ID在接受者序列里面的才能处理这个事件。

猜你喜欢

转载自blog.csdn.net/qq_53509791/article/details/129124791