React高频面试题总结 (附答案及原理代码)

1,962 阅读36分钟

React高频面试题总结 (附答案及原理代码)

1、说说对React的理解?有哪些特性?

  • React是用于构建用户界面的JavaScript库,只提供了UI层面的解决方案,遵循组件设计模式、声明式编程范式和函数式编程概念,使得前端应用程序更加高效。
  • 使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流,帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面。
  • 特性:JSX语法、单项数据绑定、虚拟DOM、声明式编程、Component

2、说说React diff算法是怎么运作的?

1、diff算法是为了节省性能而设计的,通过同层级进行比较,不跨层级使得性能更加高效

2、运作流程主要分为三层:tree层、component层、element层

    1. tree层:tree层对于DOM节点的跨层级移动的操作忽略,只对相同层级的DOM节点进行比较,一旦发现节点不存在,直接删除该节点以及以下的所有子节点
    1. component层:遇到同一个类型的组件遵循tree diff,进行层级对比,遇到不同类型的组件,直接将这个不同的组件判断为脏组件,并且替换该组件之下的所有的子节点,当知道这个组件的虚拟DOM没有任何变化,就可以手动使用,shouldComponentUpdate来判断是否需要进行diff,进一步提升了diff效率和性能
    1. element层:低于同一层级,面对全新节点,可以实现插入的操作;面对多余的节点,执行删除操作;面对换位的节点,执行移动的操作

3、 说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?

1、React生命周期主要分为三个阶段:创建阶段、更新阶段和卸载阶段

2、创建阶段:

  • 1、constructor:用来定义状态,或者是用来存放一些this的方法;

  • 2、getDerivedStateFromProps():将来会使用的,需要返回一个新的对象作为新的state或者返回null表示state状态不需要更新

  • 3、render():类组件必须实现的方法,用于渲染DOM结构,可以访问组件state与prop属性

  • 4、componentDidMount():用于执行一些数据获取,事件监听等操作

3、更新阶段:

  • 1、getDerivedStateFromProps():将来会使用的,需要返回一个新的对象作为新的state或者返回null表示state状态不需要更新
  • 2、shouldComponentUpdate:用于告知组件本身基于当前的props和state是否需要重新渲染组件,默认情况返回true
  • 3、render:类组件必须实现的方法,用于渲染DOM结构,可以访问组件state与prop属性
  • 4、getSnapshotBeforeUpdate:该周期函数在render后执行,执行之时DOM元素还没有被更新,目的在于获取组件更新前的一些信息,比如组件的滚动位置之类的,在组件更新后可以根据这些信息恢复一些UI视觉上的状态
  • 5、componentDidUpdate:可以根据前后的props和state的变化做相应的操作,如获取数据,修改DOM样式等

4、卸载阶段:

componentWillUnmount:此方法用于组件卸载前,清理一些注册是监听事件,或者取消订阅的网络请求等,一旦一个组件实例被卸载,其不会被再次挂载,而只可能是被重新创建

4、说说你对React中虚拟dom的理解?

  • 1、虚拟DOM用js对象的形式,来模拟页面dom的嵌套关系;
  • 2、虚拟DOM是一棵虚拟的JavaScript对象树,画重点,”虚拟的“、”JS对象“,指的是它把真实的网页文档节点,虚拟成一个个的js对象,并以树型结构,保存在内存中。
  • 3、实现原理:通过JS模拟网页文档节点,生成JS对象树(虚拟DOM),然后再进一步生成真实的DOM树,再绘制到屏幕。如果后面有内容发生改变,React会重新生成一棵全新的虚拟DOM树,再与前面的虚拟DOM树进行比对diff,把差异的部分打包成patch,再应用到真实DOM,然后渲染到屏幕浏览器。

5、 说说你对react hook的理解?

  • 1、Hook是React 16.8.0版本增加的新特性/新语法,可以让你在函数组件中使用 state 以及其他的 React 特性 常见的Hook:
  • 2、useState:跟类组件中state类似,方便我们定义初始化的数据,接收两个参数,一个是初始化数据,另一个是修改数据的方法
  • 3、useEffect:副作用函数,只有使用之后,才会产生副作用,他接受两个参数,一个是函数,另一个是要监听的数据,可以是[],表示只执行一次,也可以传参,传参之后只要当我们的数据发生变化时才会触发,如果不写,那么发生一次变化就会执行一次
  • 4、useMemo:数据缓存,当我们进行组件通信时,如果我们父组件中的数据发生变化,那么我们的子组件也会随着进行更新,不管我们更新的数据是否跟我们的子组件的数据有关系,他都会进行更新操作,这时候,就会存在更新性能的浪费,我们可以使用usememo来进行缓存,减少不必要的更新操作,他的缓存的参数是一个字符串,如果是一个函数的话,那么我们的usememo就会失效,这时候就需要使用useCallback进行数据的缓存操作
  • 5、useRef:可以监听我们的输入框数据的变化,获取输入框中的值

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

React组件通信就是值组件通过某种方式来传递信息以达到某个目的

方式:

  • 1、父组件向子组件传递信息:由于react数据流动是单向的,父组件在调用子组件时,只需要在子组件标签内传递参数,子组件通过props属性接收
  • 2、子组件向父组件传递信息;父组件向子组件传递一个函数,然后通过这个函数的回调,拿到子组件传过来的值
  • 3、兄弟组件之间的传递:父组件作为中间层来实现数据的互通,通过使用父组件传递
  • 4、父组件向后代组件传值:使用context提供的组件通信的一种方式,可以实现数据的共享,Provider组件通过value属性传递给后代组件
  • 5、非关系组件传递数据:将数据进行一个全局的资源管理,从而实现组件间的通信功能,例如redux

7、 说说你对受控组件和非受控组件的理解?应用场景?

1、受控组件:在React中,可变状态通常保存在组件的状态属性中,并且只能使用 setState() 更新,而呈现表单的React组件也控制着在后续用户输入时该表单中发生的情况,以这种由React控制的输入表单元素而改变其值的方式,称受控组件。简单说就是收到setState的控制,组件的状态全程响应外部数据 2、非受控组件:表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值(使用 ref从DOM获取表单值),不受setState的控制,一般在初始化的时候接收外部的数据,然后自己在内部存储其自身的状态 应用场景:

  • 1、受控组件:强制输入格式、一个数据的多个输入、动态输入、提交时的验证问题
  • 2、非受控组件:一次性取值(提交时)、提交时的验证

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

  • 1、Connect连接redux和react,包裹在我们容器组件外层,接收上边的Provider提供的store里state和dispatch,传给一个构造函数,返回一个对象,以属性的形式传递给我们的容器组件
  • 2、Connect是一个高阶函数,首先传入mapStateToProps、mapDispatchToProps,然后返回Component函数,然后将真正的Component作为参数传入,从而返回一个新的组件

9、说说react 中jsx语法糖的本质?

  • 1、Jsx的本质就是函数React.createElement的语法糖,所有的jsx语法都会最终经过babel.js转化为React.createElement这个函数调用
  • 2、三个参数:type是指的当前的元素类型,config是jsx属性,以对象的属性和值的形式存储,children是存放在标签中的内容
  • 3、jsx写法:必须引入babel并且设置script标签的type为text/bable babel:将jsx转化为React.createElement()这种函数的调用

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

  • 1、Redux中间件就是介于应用系统和系统软件之间的一类软件,使用系统软件提供的基础服务,衔接网络上应用系统的各个部分或者是不同的应用,达到资源共享,功能共享的目的
  • 2、常用的中间件:redux-thunk用于异步操作、redux-logger用于日志的记录

实现原理:所有中间件被放进一个数组中嵌套执行,判断传递过来的数据类型,最后执行store.dispatch,中间件内部middleware API可以拿到getstate和dispatch方法。

11、说说AMD、CMD、commonJS模块化规范的区别?

1、AMD:这种规范就是异步的加载模块,先定义所有依赖,然后加载完成后的回调函数中执行require([‘xxx’],function(){})

2、CMD:依赖就近原则,用的时候在写 function(require,exports,module) {require(‘xxx’)}

3、commonJS:加载模块使用require(‘xxx’)

区别:

  • 1、AMD和CMD最大的区别就是对依赖模块的执行时机处理不同,二者均为异步加载模块
  • 2、AMD依赖前置,js可以方便的知道依赖模块是谁,立即加载,CMD就近依赖,需要把模块变为字符串解析
  • 3、commonJS是所有代码都运行在模块作用域,不会污染全局作用域,模块加载是同步的,只有加载完成后才可以执行后边的操作,requeire的值是被输出的值的拷贝,模块内容变化也不会影响这个值

12、说说package.json中版本号的规则?

  • ^:只会执行不更改左边非零数字的更新
  • ~:如果写入的是~0.13.0,则当运行npm update时i,会更新到补丁版本
  • >:接收高于指定版本的任何版本
  • >=:接受等于或者高于指定版本的任何版本
  • <=:接受等于或者低于指定版本的任何版本
  • <:接受低于指定版本的任何版本
  • =:接收确切的版本
  • -:接受一定范围的版本
  • ||:组合集合
  • 无符号:接收指定的特定版本
  • Latest:使用可以用的最高版本

13、说说React jsx转换成真实DOM的过程?

  • 1、使用React.createElement或jsx编写react组件,实际上所有的jsx代码最后都会转换成React.createElement(...),babel帮助我们完成转换的过程
  • 2、createElement函数对于key和ref等特殊的props进行处理,并获取defaultProps对默认的props进行赋值,并且对传入的子节点进行处理,最终构成一个虚拟DOM对象
  • 3、ReactDOM.render将生成好的虚拟DOM渲染到指定的容器上,其中采用了批处理,事务等机制并且对特定的浏览器进行了性能优化,最终转换为真实DOM

14、说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?

1、React-redux是官方react UI绑定层,允许React组件从redux存储中读取数据,并将操作分派到存储以更新的状态。提供了connect,Provider等API,帮助我们连接react和redux,实现的逻辑会更加的严谨高效

2、@reduxjs/tookit是对Redux的二次封装,开箱即用的一个高效的Redux开发工具,使得创建store,更新store

区别:

  • 1、reduxjs/tookit相对于react-redux来说比较方便,集成了redux-devtools-extension,不需要额外的配置,非常方便
  • 2、reduxjs/tookit集成immutable-js的功能,不需要安装配置使用,提升了开发效率
  • 3、reduxjs/tookit集成了redux-thunk的功能
  • 4、reduxjs/tookit将types、actions、reducers放在一起组成了全新的slices,简化了我们的使用

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

Render函数在react中有两种形式,在类组件中指的是render方法,在函数组件中,指的是函数组件本身,在render中我们会编写jsx,jsx通过babel编译后就会转化为我们熟悉的js格式,在render过程中,React将新调用的render函数返回的树与旧版本的树进行比较,决定如何更新DOM,然后进行diff比较,更新DOM树

触发时机:

  • 1、类组件调用setState修改状态时;
  • 2、函数组件通过useState Hook修改状态时;
  • 3、类组件重新渲染时;
  • 4、函数组件重新渲染时;

16、 React性能优化的手段有哪些?

  • 1、通过shouldComponentUpdate:对比state和props,确定是否重新渲染,默认返回true,不希望渲染返回false
  • 2、PureComponent:通过对比state和props间比较结果来实现
  • 3、React.memo:缓存组件的渲染,避免不必要的更新(只能用于函数组件)
  • 4、避免使用内联函数:每次调用render时就会重新渲染一个新的函数,在组件内部创建一个函数,将事件绑定到该函数本身
  • 5、使用React Fragments避免额外标记
  • 6、使用Immutable:减少渲染的次数
  • 7、懒加载组件:使用suspense和lazy组件实现代码拆分功能
  • 8、事件绑定方式
  • 9、服务端渲染

17、如何通过原生js实现一个节流函数和防抖函数?

1、函数节流: 频繁触发,但只在特定的时间内才执行一次代码
2、函数防抖: 频繁触发,但只在特定的时间内没有触发执行条件才执行一次代码

***节流代码
const  thr (func,wait = 50)=>{
    let timer = true 
    return (...args)=>{
        if(timer){
            setTimerout(()=>{
                func.apply(this,args)
                timer = true
            },wait)
        }
        timer = false
    }
}
*** 防抖代码
const deb (func,wait = 50){
    let timer = null 
    return (...args)=>{
        if(timer){
            clearTimeout(timer)
        }
        timer = setTimeout(()=>{
            func.apply(this,args)
        },wait)
    }
}
3、区别:两者区别在于函数节流是固定时间做某一件事,比如每隔1秒发一次请求。而函数防抖是在频繁触发后,只执行一次(两者的前提都是频繁触发)
4、节流应用场景:函数节流的应用场景一般是onrize,onscroll等这些频繁触发的函数,比如你想获取滚动条的位置,然后执行下一步动作
5、防抖应用场景:输入框搜索自动补全事件,频繁操作点赞和取消点赞等等,这些应用场景,也可以通过函数节流来实现,频繁操作点赞和取消点赞,因此需要获取最后一次操作结果并发送给服务器

18、说说你对koa中洋葱模型的理解?

  • 1、在koa中,中间件被next()方法分成两部分,next方法上面会先执行,下边部门会在后续中间件执行全部结束后再执行,
  • 2、在洋葱模型中,每一层相当于一个中间件,用来处理特定的功能,比如错误处理,session处理等,其处理顺序先是next()请求,然后执行next()函数,最后是next()响应,也就是说每一个中间件都有两次处理时机。
  • 3、洋葱模型的核心原理是借助componse方法

19、说说如何借助webpack来优化前端性能?

  • 1、压缩代码:删除多余的代码、注释、简化代码的写法等等⽅式。可以利⽤webpack的 UglifyJsPlugin 和 ParallelUglifyPlugin 来压缩JS⽂件
  • 2、利⽤ cssnano (css-loader?minimize)来压缩css
  • 3、利⽤CDN加速: 在构建过程中,将引⽤的静态资源路径修改为CDN上对应的路径。可以利⽤webpack对于 output 参数和各loader的 publicPath 参数来修改资源路径
  • 4、Tree Shaking: 将代码中永远不会⾛到的⽚段删除掉。可以通过在启动webpack时追加参数 --optimize-minimize 来实现
  • 5、Code Splitting: 将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利⽤浏览器缓存
  • 6、提取公共第三⽅库: SplitChunksPlugin插件来进⾏公共模块抽取,
  • 7、利⽤浏览器缓存可以⻓期缓存这些⽆需频繁变动的公共代码

20、说说react router有几种模式?实现原理?

两种模式:hash模式和history模式

  • Hash原理:hash 值改变,触发全局 window 对象上的 hashchange 事件。所以 hash 模式路由就是利用 hashchange 事件监听 URL 的变化,从而进行 DOM 操作来模拟页面跳转
  • History原理:利用了 HTML5 History Interface 中新增的 pushState () 和 replaceState () 方法。

21、说说对React Hooks的理解?解决了什么问题?

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

解决问题:

  • 1、难以重用和共享组件中与状态相关的逻辑
  • 2、由于业务变动,函数组件不得不改为类组件
  • 3、逻辑复杂的组件难以开发和维护,当组件需要处理多个不相关的local state时,每个生命周期函数中肯能会包含各种互不相关的逻辑
  • 4、类组件在基于现有工具的优化上存在的些许问题
  • 5、hooks的出现,使函数组件的功能得到了扩充,拥有了类组件相似的功能,在我们日常使用中,使用hooks能够解决大多数问题,并且还拥有代码复用机制,因此优先考虑hooks

22、说说 React 性能优化的手段有哪些?

  • 1、通过shouldComponentUpdate:对比state和props,确定是否重新渲染,默认返回true,不希望渲染返回false
  • 2、PureComponent:通过对比state和props间比较结果来实现
  • 3、React.memo:缓存组件的渲染,避免不必要的更新(只能用于函数组件)
  • 4、避免使用内联函数:每次调用render时就会重新渲染一个新的函数,在组件内部创建一个函数,将事件绑定到该函数本身
  • 5、使用React Fragments避免额外标记
  • 6、使用Immutable:减少渲染的次数
  • 7、懒加载组件:使用suspense和lazy组件实现代码拆分功能
  • 8、事件绑定方式
  • 9、服务端渲染

23、说说 React中的setState执行机制?

  • 一个组件显示形态就可以由数据状态和外部参数决定,而数据状态就是state,当需要修改值的状态就通过seState来改变,从而达到更新组件内部数据的作用

24、说说React生命周期中有哪些坑?如何避免?

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

25、调和阶段setState干了什么?

  • 1、 代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。
  • 2、 经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面;
  • 3、 在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染;
  • 4、 在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。

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

    1. 将应用的状态统一放到state中,由store来管理state。
    1. reducer的作用是返回一个新的state去更新store中对用的state
    1. 按redux的原则,UI层每一次状态的改变都应通过action去触发,action传入对应的reducer 中,reducer返回一个新的state更新store中存放的state,这样就完成了一次状态的更新
    1. subscribe是为store订阅监听函数,这些订阅后的监听函数是在每一次dipatch发起后依次执行
    1. 可以添加中间件对提交的dispatch进行重写
state
1、state就是store中存储的数据,store里可以拥有多个state,Redux规定一个state对应一个View,只要state相同,view就是一样的,反过来也是一样的,可以通过store.getState()获取

import {createStore} from 'redux'
const store=createStore(fn);
const state=store.getState()


Action
1、action可以改变state,而且也是改变state的唯一方法

const action={
      type:'ADD_TODO', //action名称,必须存在
      payload:'redux'
}


dispatch
1、store.dispatch( )是view发出Action的唯一办法
2、在view中,用户触发一个行为或者简单理解点击某一个按钮,这时候如果需要修改state值,就需要触发action,而store.dispatch接收一个Action作为参数,将它发送给store通知store来改变state。

const action = {
        type:'ADD_TODO',
        payload:'redux'
    };
    store.dispatch(action);


reducer
1、Store收到Action以后,必须给出一个新的state,这样view才会发生变化。这种state的计算过程就叫做Reducer。
2、Reducer是一个纯函数,他接收Action和当前state作为参数,返回一个新的state。

const reducer =(state,action)=>{
      switch(action.type){
        case ADD_TODO:
            return newstate;
        default return state
      }
    }

27、 React合成事件的原理?

  • react中的事件都是合成事件,不是把每一个dom的事件绑定在dom上,而是把事件统一绑定到document中,触发时通过事件冒泡到document进行触发合成事件,因为是合成事件,所以我们无法去使用e.stopPropagation去阻止,而是使用e.preventDefault去阻止。
  • 合成事件:循环所有类型的eventPlugin,对应每个事件类型,生成不同的事件池,如果是空,则生成新的,有则用之前的,根据唯一key获取到指定的回调函数,再返回带有参数的回调函数。

28、 为什么react元素有一个$type属性?

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

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

  • Redux中间件就是介于应用系统和系统软件之间的一类软件,使用系统软件提供的基础服务,衔接网络上应用系统的各个部分或者是不同的应用,达到资源共享,功能共享的目的
  • 常用的中间件:redux-thunk用于异步操作、redux-logger用于日志的记录
  • 实现原理:所有中间件被放进一个数组中嵌套执行,判断传递过来的数据类型,最后执行store.dispatch,中间件内部middleware API可以拿到getstate和dispatch方法。

30、props和state相同点和不同点?render方法在哪些情况下会执行?

不同点:

  • 1.props不可以在组件内部修改,但state可以在组件内部修改
  • 2.可以从父组件修改自组件的props,而不能从父组件修改自组件的state

相同点:

  • 1.props和state都是导出HTML的原始数据。
  • 2.props和state都是确定性的,如果我们写的组件为同一props和state的组合
  • 3.生成了不同的输出,那木我们肯定在哪里做错了
  • 4.props和state都会触发渲染更新
  • 5.props和state都是纯JS对象(用typeof来判断,结果都是object)
  • 6.可以从父组件得到初始值props和state的初始值

render在哪些情况下执行?

  • 1、类组件调用setState修改状态时
  • 2、函数组件通过useState Hook修改状态时
  • 3、类组件重新渲染时
  • 4、函数组件重新渲染时

31、react新出来两个钩子函数是什么?和删掉的will系列有什么区别?

一、React新增的两个生命周期钩子函数?

  • 1、getDerviedStateFromProps

作用:从props中获取衍生的state

  • 2、getSnapshotBeforeUpdate

作用:在组件更新前获取快照,一般结合componentDidUpdate使用,getSnapBeforeUpdate中返回值将作为第三参数传递给componentDidUpdate 它和componentDidUpdate将render夹在中间,其实它的核心作用就是在render改变dom之前,记录更新前的dom信息传递给componentDidUpdate。

二、React新的生命周期去除的钩子函数?

  • 1、componentWillMount
  • 2、componentWillReceiveProps
  • 3、componentWillUpdate

总结

  • 1、componentWillMount中可能需要做的事(一些数据初始化的操作就应该放在这个钩子中处理),constructor与componentDidMount也能做,甚至做的更好,此方法被废弃。
  • 2、componentWillReceiveProps实际行为与命名并不相符,由于不稳定性已由getDerivedStateFromProps代替;
  • 3、而componentWillUpdate同等理由被getSnapshotBeforeUpdate代替

32、说说Real DOM和Virtual DOM的区别?优缺点?

1、Real DOM,真实DOM, 意思为文档对象模型,是一个结构化文本的抽象,在页面渲染出的每一个结点都是一个真实DOM结构
2、Virtual Dom,本质上是以 JavaScript 对象形式存在的对 DOM 的描述
3、创建虚拟DOM目的就是为了更好将虚拟的节点渲染到页面视图中,虚拟DOM对象的节点与真实DOM的属性一一照应
4、两者的区别如下:
	虚拟DOM不会进行排版与重绘操作,而真实DOM会频繁重排与重绘
	虚拟DOM的总损耗是“虚拟DOM增删改+真实DOM差异增删改+排版与重绘”,真实DOM的总损耗是“真实DOM完全增删改+排版与重绘”
优缺点?
	真实DOM的优势:易用
	缺点:
		1、效率低,解析速度慢,内存占用量过高
		2、性能差:频繁操作真实DOM,易于导致重绘与回流
	使用虚拟DOM的优势如下:
		1、简单方便:如果使用手动操作真实DOM来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难
		2、性能方面:使用Virtual DOM,能够有效避免真实DOM数频繁更新,减少多次引起重绘与回流,提高性能
		3、跨平台:React借助虚拟DOM, 带来了跨平台的能力,一套代码多端运行
	缺点:
        1、在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
		2、首次渲染大量DOM时,由于多了一层虚拟DOM的计算,速度比正常稍慢

33、说说react的事件机制?

  • 1、react自身实现了一套自己的事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等,虽然和原生的是两码事,但也是基于浏览器的事件机制下完成的。
  • 2、React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
  • 3、React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()无效的原因。
  • 4、React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
  • 5、React 有一套自己的合成事件 SyntheticEvent

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

React Fiber 是 Facebook 花费两年余时间对 React 做出的一个重大改变与优化,是对 React 核心算法的一次重新实现。

在react中,主要做了以下的操作:

  • 1、为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新,注意是重新执行优先级低的任务
  • 2、增加了异步任务,调用requestIdleCallback api,浏览器空闲的时候执行 dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行
  • 3、从架构角度来看,Fiber 是对 React核心算法(即调和过程)的重写
  • 4、从编码角度来看,Fiber是 React内部所定义的一种数据结构,它是 Fiber树结构的节点单位,也就是 React 16 新架构下的虚拟DOM

35、说说react diff的原理是什么?

1、跟Vue一致,React通过引入Virtual DOM的概念,极大地避免无效的Dom操作,使我们的页面的构建效率提到了极大的提升而diff算法就是更高效地通过对比新旧Virtual DOM来找出真正的Dom变化之处

运作流程主要分为三层:tree层、component层、element层

  • 1、tree层:tree层对于DOM节点的跨层级移动的操作忽略,只对相同层级的DOM节点进行比较,一旦发现节点不存在,直接删除该节点以及以下的所有子节点
  • 2、component层:遇到同一个类型的组件遵循tree diff,进行层级对比,遇到不同类型的组件,直接将这个不同的组件判断为脏组件,并且替换该组件之下的所有的子节点,当知道这个组件的虚拟DOM没有任何变化,就可以手动使用,shouldComponentUpdate来判断是否需要进行diff,进一步提升了diff效率和性能
  • 3、element层:低于同一层级,面对全新节点,可以实现插入的操作;面对多余的节点,执行删除操作;面对换位的节点,执行移动的操作

36、shouldComponentUpdate有什么作用?

  • 1、根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。默认行为是 state 每次发生变化组件都会重新渲染。
  • 2、eact中props,state值的变化,会导致组件重新渲染。使用shouldComponentUpdate就是为了减少render不必要的渲染。 返回布尔值,然后做 Virtual DOM 比较,并得出是否需要做真实 DOM 更新

37、在使用redux过程中,如何防止定义的action-type的常量重复?

  • ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。
  • Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

38、说说React中的虚拟dom?在虚拟dom计算的时候diff和key之间有什么关系?

1、实际上它只是一层对真实DOM的抽象,以JavaScript对象(VNode节点)作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作时这棵树映射到真实环境上,创建虚拟DOM就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟DOM对象的节点与真实DOM的属性一一照应

2、通过JS模拟网页文档节点,生成JS对象树(虚拟DOM),然后再进行一步生成真实的DOM树,再绘制到屏幕。如果后面有内容发生改变,React会重新生成一棵全新的虚拟DOM树,再与前面的虚拟DOM树进行比对diff,把差异的部分打包成patch,再应用到真实DOM,然后渲染到屏幕浏览器。 在虚拟dom计算的时候diff和key之间有什么关系:

  • 1、React需要同时维护两棵虚拟DOM树:一棵表示当前的DOM结构,另一棵在React状态变更将要重新渲染时生成。React通过比较这两棵树的差异,决定是否需要修改DOM结构,以及如何修改。这种算法称作Diff算法。
  • 2、key 当同一层级的某个节点添加了对于其他同级节点唯一的key属性,当它在当前层级的位置发生了变化后。react diff算法通过新旧节点比较后,如果发现了key值相同的新旧节点,就会执行移动操作(然后依然按原策略深入节点内部的差异对比更新),而不会执行原策略的删除旧节点,创建新节点的操作。这无疑大大提高了React性能和渲染效率。

39、React的props.children使用map函数来遍历会收到异常显示,为什么?应该 如何遍历?

  • 1、当前组件没有子节点数据类型就是undefined,
  • 2、有一个子节点数据类型就是object 。
  • 3、有多个子节点的时候才会是array ,只有在多个节点的时候才可以直接调用map方法,react资深提供了一个react.children.map()方法,可以安全遍历子节点对象。

40、谈谈你对immutable.js的理解?

1、Immutable.js就是react中用来做性能优化的

2、在react中使用immutable可以减少页面的一些不必要的渲染,不需要像react中进行深度对比而进行渲染

3、immutable可以直接快速的进行对比,完成渲染,提升了渲染的效率,降低了性能的消耗

4、Immutable不可改变的,在计算机中,即指一旦创建,就不能再被更改的数据 对 Immutable对象的任何修改或添加删除操作都会返回一个新的 Immutable对象 Immutable 实现的原理是 Persistent Data Structure(持久化数据结构):

  • 1、用一种数据结构来保存数据
  • 2、当数据被修改时,会返回一个对象,但是新的对象会尽可能的利用之前的数据结构而不会对内存造成浪费

41、redux本来是同步的,为什么它能执行异步代码?实现原理是什么?中间件的 实现原理是什么?

  • 1、Redux-thunk这个中间件支持异步操作
  • 2、执行异步的操作首先需要下载一个thunk,通过thunk来进行异步的一个操作,支持异步操作,可以使用dispatch和getState来进行数据的获取或状态
  • 3、Redux是一个状态管理库,redux的核心是store,actions,reducers三个部分
  • 4、通过dispatch发放到任务的actions中,在actions中返回一个promise对象,再通过dispatch派发到reducers中
  • 5、在reducers中通过传递type和data来进行判读

42、redux中同步action与异步action最大的区别是什么?

1、同步action:执行了dispatch函数之后,对应的reducer纯函数立即得到执行,reducer执行完了之后,state立即就改变了,此时用store.getState函数,取到的是最新的state值;

2、异步action:原则上redux并没有提供异步action的处理方案,异步的action需要依赖第三方的中间件解决(如redux-thunk),dispatch了一个异步action(本质上是dispatch的一个函数)之后,目标state并不会立即响应,而是要看异步函数内部的逻辑,来决定state什么时候响应。

3、区别:

  • 首先区别redux和react-redux,redux是一个单独的模块,在其他框架中也能使用,而react-redux是为react管理数据而生。
  • redux的设计思想:web应用是一个状态机,视图与状态是一一对应的,所有的状态,保存在一个对象里面

43、redux-saga和redux-thunk的区别与使用场景?

1、使用redux-thunk的代码,当我们返回的是函数时,store会帮我们调用这个返回的函数,并且把dispatch暴露出来供我们使用。对于redux-thunk的整个流程来说,它是等异步任务执行完成之后,我们再去调用dispatch,然后去store去调用reduces

2、使用了redux-saga的代码,当我们dispatch的action类型不在reducer中时,redux-saga的监听函数takeEvery就会监听到,等异步任务有结果就执行put方法,相当于dispatch,再一次触发dispatch。对于redux-saga的整个流程来说,它是等执行完action和reducer之后,判断reducer中有没有这个action

区别:

  • 1、redux-thunk和redux-saga处理异步任务的时机不一样。对于redux-saga,相对于在
  • 2、redux的action基础上,重新开辟了一个 async action的分支,单独处理异步任务
  • 3、saga 自己基本上完全弄了一套 asyc 的事件监听机制,代码量大大增加,从我自己的使用体验来看 redux-thunk 更简单,和 redux 本身联系地更紧密。

44、为什么普通的for循环比forEach循环性能要高?

1、for循环就是通过下标,对循环中的代码反复执行,功能强大,可以通过index取得元素。处理比较复杂的处理的时候比较方便

2、forEach()循环方法用于调用数组的每个元素,并将元素传递给回调函数。foreach有的也叫做增强for循环,forEach其实是for循环的一个特殊简化版。forEach循环对于空的数组是不会执行回调函数的。

区别:

  • 1、遍历:for循环按照顺序进行遍历,forEach使用iterator迭代器遍历
  • 2、数据结构:for循环是随机访问元素,foreach是顺序链表访问元素
    1. 性能上:对于数组arraylist来说,是顺序表,使用for循环可以进行顺序访问,速度比较快;使用foreach循环会比for循环稍微慢一点。对于linedlist来说,是单链表,使用for循环每次都要从第一个元素读取next域来读取,速度非常慢;使用foreach可以直接读取当前的节点,数据较快。

45、说说你对useEffect的理解,可以模拟哪些生命周期?

  • 1、使用钩子函数useEffect可以实现组件的副作用。useEffect(希望执行的动作, [组件状态的列表]);第二个参数用来处理useEffect调用的时机,是一个数组,数组内是组件状态的列表。
  • 2、useEffect模拟componentDidMount:当useEffect的第二个参数传入空列表时,相当于模拟生命周期函数componentDidMount。这个副作用仅在组件第一次挂载ui的时候调用一次。用它来模拟组件初次挂载时,访问api、获得数据:
  • 3、useEffect模拟componentDidUpdate:如果在使用useEffect时不带上第二个参数,就相当于模拟了生命周期函数componentDidUpdate。每次渲染结束的时候都会被调用。
  • 4、useEffect模拟componentWillUnmount,在useEffect中返回一个函数用于模拟component WillUnMount

46、说说React中setState和replaceState的区别?

1、setState用于设置状态对象

2、两个参数:nextState,将要设置的新状态,该状态会和当前的state合并;callback,可选参数,回调函数。该函数会在setState设置成功,且组件重新渲染后调用。

3、合并nextState和当前state,并重新渲染组件。setState是React事件处理函数中和请求回调函数中触发UI更新的主要方法。

4、replaceState()方法与setState()类似,但是方法只会保留nextState中状态,原state不在nextState中的状态都会被删除

5、两个参数:nextState,将要设置的新状态,该状态会替换当前的state。callback,可选参数,回调函数。该函数会在replaceState设置成功,且组件重新渲染后调用。


  1. setState 是修改其中的部分状态,相当于 Object. assign,只是覆盖,不会减少原来的状态;
  2. replaceState 是完全替换原来的状态,相当于赋值,将原来的 state 替换为另一个对象,如果新状态属性减少,那么 state 中就没有这个状态了。

47、说说react中onClick绑定后的工作原理?

当组件元素绑定onClick事件之后:

  • 1、react会对事件先进行注册,将事件统一注册到document上
  • 2、根据组件唯一的表示key来对事件函数进行存储
  • 3、统一的指定,dispatchEvent回调函数
  • 4、存储事件回调:react会将click这个事件统一存放在一个对象中,回调函数中存储采用键值对的方式存储在对象中,key是组件唯一标识id,value对应的事件就是回调函数,通过组件的key就能回到相对应的函数了。

48、 useEffect的依赖为引用类型如何处理

  • 1、使用 ‘use-deep-compare-effect’ :他可以进行深比较,使用方法也很简单, import useDeepCompareEffect from ‘use-deep-compare-effect’ 之后用useDeepCompareEffect 替换掉 原有的 useEffect 即可。
  • 2、也可以使用 useRef这个钩子来解决上述问题,useRef的特性是跨渲染周期保存数据
  • 3、用useRef保存了之前的数据, useEffect中的依赖以然为引用类型, 每次obj发生改变都会调用执行函数,但是执行函数中多了一个判断, prevObj是上一次渲染时obj的值, 用prevObj中的某个key与此次obj中的某个key做对比,满足条件后做其他操作

49、知道react里面的createPortal么,说说其使用场景?

  • 1、Portal ,将子节点渲染到存在于父组件以外的 DOM 节点。
  • 2、ReactDOM.createPortal(child, container) 第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 DOM 元素。
  • 3、Portals 适合脱离文档流(out of flow) 的组件,特别是 position: absolute 与 position: fixed的组件。比如模态框,通知,警告,goTop 等 <html>