一、 组件基础
1.React事件机制
(1)事件委托,它将所有所有事件委托给document这个顶级dom上,减少页面的注册事件数量,减少内存开销,优化浏览器性能,也为了更好管理
(2)事件冒泡,在document上监听所有事件,事件发生并且冒泡到document处时,react将事件内容封装并交由真正的处理函数运行,这样不仅减少了内存消耗,还能在组件挂载和销毁时统一订阅和移除事件,由于合成事件是通过事件冒泡触发的,所以阻止默认事件是无效的,而应该用阻止事件冒泡的方法
(3)合成事件,解决了浏览器之间的兼容问题,对原生浏览器事件来说,浏览器会给监听器创建一个事件对象,如果有很多事件监听,就需要分配很多事件对象,造成高额的内存分配问题,但是对合成事件来说,有一个事件池专门管理合成事件的创建和销毁,当事件被使用时,就从中复用对象,事件回调结束后,销毁事件对象上的属性便于下次复用
2.React的事件和普通的HTML事件有什么不同?
(1)区别
①对于事件名称的命名方式,原生事件为全小写,react事件采用小驼峰;
②对于事件函数处理语法,原生事件为字符串,react事件为函数
③react事件不能采用return false的方式来阻止浏览器的默认行为,而应该用preventDefault()阻止事件冒泡来阻止默认行为
④合成事件是react模拟原生DOM事件所有能力的一个事件对象,其优点如下:
1)兼容所有浏览器,更好的跨平台
2)将事件统一存放在一个数组,避免频繁的新增与删除(垃圾回收)
3)方便react统一管理和事务机制
4)事件的执行顺序为原生事件先执行,合成事件后执行,合成事件会冒泡绑定到document上,所以尽量避免原生事件与合成事件混用,如果原生事件阻止冒泡,可能导致合成事件不执行,因为需要冒泡到document上合成事件才会执行
3.react组件中怎么做事件代理?它的原理是什么?
(1)通过合成事件模拟原生dom事件所有能力
(2)React给予V DOM(虚拟dom)实现了一个合成事件层,定义的事件处理器会接收一个合成事件对象的实例,它符合W3C标准,且与原生的浏览器事件拥有同样的接口,支持冒泡机制,所有的事件都自动绑定在最外层上
(3)在react底层,主要对合成事件做了两件事
①事件委托,react会把所有的事件绑定到结构的最外层,使用统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部事件监听和处理函数
②自定绑定,react组件中,每个方法的上下文都会指向该组件的实例,即自定绑定this指向当前组件
4.React高阶组件、Render props、hooks有什么区别,为什么要不断迭代
(1)React高阶组件是React用于复用组件逻辑的一种技巧,其本质是接收一个组件作为参数,返回一个组件的函数,可以理解为“为组件添加某种功能”的函数
(2)优点:逻辑复用,不影响被包裹组件的内部逻辑
(3)缺点:如果包裹组件和高阶组件传递的props如果重名的话会被覆盖
(4)Render props是一种在react组件之间使用的一个值为prop共享代码技术,具体来说就是:Render prop是一个告知组件需要渲染什么内容的函数prop
(5)优点:数据共享,逻辑复用
(6)缺点:无法在return语句外访问数据,嵌套
(7)Hooks是react 16.8中新增的特性,它是专门给函数组件使用,让函数组件拥有类组件相同的功效的,因为函数组件整个相当于类组件的render(),所以类组件一开始没有生命周期,state之类的
(8)优点:使用直观,不存在hoc重名问题,不存在嵌套问题,能在return之外访问数据
(9)三者的区别:三者都能用来进行逻辑复用,区别在于高阶组件为接收组件,对其进行包装,Render props为在render中渲染共享数据,而hooks是为函数组件准备的,以函数调用的形式共享数据
(10)为什么要不断迭代?
①大部分情况下,高阶组件和Render props都存在各自的缺陷
②重名问题,嵌套问题,无法在return外访问数据,数据来源不清晰
③不断迭代就是为了解决上述问题,让我们能更加简洁的方式实现逻辑复用,降低开发难度
5.对React-Fiber的理解,它解决了什么问题?
(1)JS引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行时,另一个线程只能挂起等待,如果js线程长时间的占用主线程,那么渲染层的更新就不得不长时间的等待,导致页面响应变慢,卡顿。
(2)React在渲染组件时,整个过程一气呵成无法中断,如果组件较大,js线程就会一直执行,等到整个虚拟DOM计算完毕,才交给渲染的线程,这就会导致用户交互,动画等任务无法立即处理,页面卡顿
(3)React-Fiber是react核心算法的一次重新实现,主要做了以下操作
①为每个任务增加了优先级,优先级高的任务可以中断优先级低的任务,然后再重新执行优先级低的任务
②增加了异步任务,调用requestldleCallback api,浏览器空闲的时候执行
③dom diff树变成了链表,一个dom对应两个fiber(一个链表,对应两个队列),这都是为了找到被中断的任务重新执行
④总结:Fiber算法将原有渲染过程进行分割切片,让每个步骤都能插入额外操作
6.React.Component和React.PureComponent的区别
(1)React.Component和React.PureComponent几乎完全相同,但React.PureComponent通过prop和state的浅对比来实现shouldComponentUpate(),如果React组件的render()函数在给定相同的props和state下渲染为相同结果,就可以使用React.PureComponent来提升性能
(2)总结:PureComponent相比Component会自动判断是否执行shouldComponentUpate(),但仅限当前组件,深层次组件如果也需要自动执行,就需要继续从PureComponent继承
7.Component(组件)、Element(元素)、Instance(组件实例)之间有什么区别和联系
(1)一个元素(Element)是一个普通对象,描述了对于一个DOM节点或者其他组件,你想让它在屏幕上成现什么样子就什么样,元素可以在它的属性中包含其他元素用于形成元素树,创建一个element成本很低,创建之后不可改变
(2)一个组件(component)可以通过多种方式声明,可以是带有一个render()方法的类,简单点也可以定义为一个函数,这两种情况下,它都把props作为输入,把返回的元素树作为输出
(3)一个实例(Instance)是你所写的组件类(component class)中使用关键字this所指向的对象
(4)函数式组件没有实例,类组件才有,但是不需要直接创建一个组件的实例,因为react帮我们做了这些
8.React.createClass和extends Component的区别有哪些?
(1)createClass本质上是一个工厂函数,extends的方式更接近最新的ES6规范的class写法,两种方式在语法上的差别主要体现在方法的定义和静态属性的声明上。createClass方式的方法定义用逗号隔开,因为createClass本质上是一个函数,传递给它的是一个Object,而class的方式定义方法时务必不能用逗号隔开,因为这是ES6的class语法规范
(2)主要区别在于
①语法区别
1)const App = React.createClass({
render(){
return (
)}
})
2)class App extends React.Component{
constructor(props){super(props)}
render(){
return (
)}
}
②propType和getDefaultProps
1)React.createClass通过proTypes对象和getDefaultProps()方法来设置和获取props
2)React.Component通过设置两个属性propTypes和defaultProps
③状态的区别
1)React.createClass通过getInitialState()方法返回一个包含初始值的对象
2)React.Component通过constructor设置初始状态
④this的区别
1)React.createClass会正确绑定this
2)React.Component由于使用了ES6,this不会自动绑定到React类的实例上,通常需要bind(this)更改它的this指向
⑤Mixins
1)React.createClass创建组件的话,可以在创建组件时添加一个叫做mixins的属性,并将可供混合的类的集合以数组的形式赋值给mixins
2)React.Component创建就不能使用mixins特性
9.React高阶组件是什么?和普通组件有什么区别?适用什么场景?
(1)高阶组件(HOC)是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件,它只是一种模式,这种模式是由React自身的组合性质产生的,我们将它们称为“纯组件”,它接收任何动态提供的子组件,但他们不会修改或复制其输入组件的任何行为
(2)用例:
①代码复用,逻辑抽象化
②渲染劫持
③抽象化和操作状态(state)
④操作属性(props)
10.对componentWillReceiveProps的理解
(1)在react的componentWillReceiveProps(nextProps)生命周期中,可以在子组件的render函数执行前,通过this.props获取旧的属性,通过nextProps获取新的props,对比两次props是否相同,从而更新子组件自己的state
(2)这样的好处是,可以将数据请求放在这里执行,需要传的参数则从componentWillReceiveProps(nextProps)中获取,而不必将所有的请求放在父组件中。该请求只会在该组件渲染时才会发出,从而减轻请求负担。
11.哪些方法会触发React重新渲染?重新渲染的render会做些什么?
(1)哪些方法会触发react重新渲染?
①setState()方法被调用
②父组件重新渲染
(2)重新渲染render会做些什么?
①会对新旧VNode进行对比,也就是我们所说的Diff算法
②对新旧两棵树进行一个深度优先遍历,这样每一个节点都会一个标记,在到深度遍历的时候,每遍历到一个节点,就把该节点和新的节点树进行对比,如果有差异就放到一个对象里
③遍历差异对象,根据差异的类型,根据相应规则更新VNode
12.React如何判断什么时候重新渲染组件
(1)组件的状态发生改变
(2)shouldComponentUpdate返回true时
13.React声明组件有哪几种方法,有什么不同?
(1)用React.Component方法以ES6的形式来定义组件
①目前极为推荐的创建有状态组件的方式
(2)用React.createClass来定义组件
①两种类组件的创建方式的区别上面有做解释
(3)用函数式定义无状态组件
①一开始存粹用来展示组件
②不涉及state状态,组件不会被实例化,性能提升
③不能访问this对象
④无法访问生命周期方法
⑤hooks出来以后,更好用
14.对有状态组件和无状态组件的理解及使用场景
(1)通常来说“状态”指的是组件内部的state
(2)有状态组件指类组件,无状态组件指函数组件
(3)通常情况下,简单展示的元素的时候,用函数组件
15.对React中Fragement的理解,它的使用场景是什么?
(1)V16之前,Fragement的创建是通过扩展包react-addons-create-fragment创建,V16以后则直接通过React.Fragment创建
(2)React.Fragment组件能够在不额外创建Dom元素的情况下,让render()方法中返回多个元素,常见模式是一个组件返回多个元素,Fragment允许将子元素分组,而无需向DOM添加额外节点
(3)V16开始,render支持返回数组
16.React如何获取组件对应的DOM元素?
(1)React.createRef();通过该api在构造器中创建一个用于绑定DOM节点的变量,通过.current来获取真实DOM
(2)React.useRef();函数组件中用来获取dom元素的hooks
17.React中可以在render访问refs吗?为什么?
(1)不可以,因为render阶段DOM还没有生成,无法获取DOM
(2)DOM的获取需要在pre-commit阶段和commit阶段
18.对React的插槽的理解,如何使用,有哪些使用场景?
(1)插槽能将子节点渲染到父节点的DOM层之外
(2)父组件传递给子组件的内容不光可以通过props,自定义属性的方式传递,还可以通过插槽
(3)应用场景
①当父组件有overflow:hidden或者z-index时,但你需要子组件在视觉上“跳出”父组件,例如:对话框,弹窗,提示框
19.在React中如何避免不必要的render?
(1)使用shouldComponentUpdate生命周期判断,通过返回true或false控制
(2)使用React.PureComponent代替React.Component控制子组件是否重新渲染
(3)useCallback()配合memo缓存组件
20.对React-Intl的理解,它的工作原理?
(1)用途:React项目国际化,react-intl这个库提供了针对组件、日期、数字、字符串等多种国际化方法
(2)类似字体文件,通过切换语言,加载不同语言到本地
(3)实现原理和react-redux的实现原理类似,最外层包一个Provider,利用getChildContext将intlConfigPropTypes存起来,在FormattedMessage、FormattedNumber等组件或者调用injectIntl生成的高阶组件中使用,来完成国际化的
(4)前端国际化核心步骤有两步:
①创建资源文件,以key-value方式存储
②加载资源文件,将页面上key的内容替换为相关value
21.对React context的理解
(1)父子组件通信是通过props进行数据传递
(2)子父通讯是通过子组件调用父组件方法进行传递
(3)为了解决多层级组件通讯的问题,类似vue2的bus总线通讯,来跨层级传递数据
(4)通过react.createContext()创建context对象,并包含provider,Consumer两个组件作为创建上下文的容器,defaultValue设置共享默认数据
(5)useContext()用来弥补函数组件无法跟类组件一样传递参数的解决方案
22.为什么React并不推荐优先考虑使用Context?
(1)目前还处于实验阶段,后续版本会发生较大变化
(2)但对于独有组件,由于影响范围小,如果可以做到高内聚,不破坏组件树之间的依赖关系,可以考虑使用
(3)对于组件之间的数据通信或者状态管理,推荐使用props绘制state,然后再考虑使用第三方的成熟库进行解决,实在不行再使用context
(4)context的更新需要通过setState()触发,但是如果中间的子组件通过一些方法不影响更新,那么不能保证context的更新一定可以使使用Context的子组件更新
23.React中什么是受控组件和非受控组件?
(1)react中组件根据是否受react控制可分为受控组件和非受控组件
(2)受控组件
①可变状态通常保存在组件的状态属性中,并且只能通过setState()进行更新,而呈现表单的react组件也控制着后续用户输入时该表单中发生的情况,这种由react控制的输入表单元素而改变其值的方式,称为受控组件
(3)非受控组件
①表单数据由DOM本身处理,即不受setState()的控制,与传统html表单输入相似,通常使用ref来从DOM获得表单值
(4)受控组件的使用场景:一般用在需要动态设置其初始值的情况,例如某些表单信息编辑需要填充默认值
(5)非受控组件的使用场景:一般用于无任何动态初始值信息的情况,例如空input表单
24.React中refs的作用是什么?有哪些应用场景?
(1)refs是一个获取DOM节点或React元素实例化的工具
(2)适用场景:
①对DOM元素焦点的控制,内容选择或者媒体播放
②通过对DOM元素控制,触发动画特效
③通过第三方DOM库的集成
25.React中除了在构造函数中绑定this,还有别的方式吗?
(1)函数定义的时候使用箭头函数定义
(2)函数调用的时候使用bind(this)更改this指向
(3)函数调用的时候使用箭头函数调用
26.React组件的构造函数有什么用?它是必须的吗?
(1)为了符合es6的语法规范,让每个组件都是一个类,es6中想要创建一个对象,就要掉欧阳那个响应的构造函数,构造函数只会在组件初次渲染中执行一次
(2)一般有三种用途
①指定this,super(props)
②设置初始化状态
③为组件上的构造函数绑定this
27.React.forwardRef是什么?有什么作用
(1)React.forwardRef会创建一个react组件,这个组件能够将其接受的ref属性转发到其组件树下另一个组件中
(2)场景为:转发refs到DOM组件,或者在高阶组件中转发refs
(3)因为ref是类组件的关键字,默认返回组件实例,因此想在class组件让它返回非组件实例,只能通过forwardRef生成的高阶组件将ref这个关键字关联到其他地方
28.类组件与函数组件有什么异同?
(1)相同点
①都是用来返回页面中渲染的react元素,最终呈现效果相同
(2)不同点
①函数组件相当于类组件的render()部分,没有类组件的继承,实例化
②类组件有生命周期钩子函数
二、 数据管理
1.React setState调用的原理
(1)setState修改的state用的是对象的合并而不是对象修改
(2)React将多个setState的调用合并成一个来执行,并不是马上就重新渲染
(3)这个调用原理描述的不是很清楚,到底是问调用顺序,还是它的实现原理
2.React setState调用之后发生了什么?是同步还是异步?
(1)react会为当前节点创建一个updateQueue更新队列
(2)然后触发reconciliation过程,在这个过程中使用fiber算法,生成新的Fiber树
(3)然后react Scheduler会根据优先级高低,先执行优先级高的节点,具体是执行doWork方法
(4)在doWork方法中,React会执行一遍updateQueue更新队列中的方法,以获取新的节点,然后对比新旧节点,为老节点打上更新,插入,替换等标签
(5)当前节点doWork完成后,会执行performUnitOfWork方法获得新节点,然后再重复上面的过程
(6)当所有节点都doWork完成后,会执行commitRoot方法,进入commit阶段
(7)在commit阶段,react根据前面各个节点的标签,一次性更新整个dom
(8)V18以前合成事件和生命周期钩子函数中是异步,原生事件和setTimeout中异步
3.React中的setState批量更新的过程是什么?
(1)源码分析上,进行一次setState,执行了enqueueSetState()函数,函数内大概步骤是:
①createUpdate创建一个update对象
②enqueueUpdate创建updateQueue对象,将update对象存入到当前Fiber节点的updataQueue对象中的firstUpdate和lastUpdate中
③scheduleWork调用requestWork函数
④requestWork函数中判断如果这次的setState不是合成事件触发的,那么isBatchingUpdates将会为false,直接执行performSyncWork函数,马上对setState进行diff和渲染
(2)连续setState多次只触发一次render就是因为经过了合成事件的关系,合成事件先执行了onClick函数中的setState,修改了Fiber的updateQueue对象的任务,执行完onClick函数体后,再由合成事件让根Fiber进行渲染。所以无论你在一个事件内触发无数次setState,也只触发一次render。
(3)生命周期中:在生命周期内进行setState的话,当React的组件还没有渲染完成的时候,isRendering是为true
(4)结论:
①异步:React会先找到我们注册的vnode和vnode内的对应事件,从而在执行前,先把isBatchingUpdate这个变量打开,只要我们的方法没完成,由于变量锁的存在,会一直让我们的修改停留在更新状态中,一直不会更新到实际的state上,直到我们的方法执行完,事务的后置函数会关闭我们的isBatchingUpdate,并执行渲染操作,至此整个批量更新就完成了
②同步:setTimeout里面同步是由于setTimeout会把里面的函数放到下一个宏任务中,这样就刚好跳出了事务的控制,就会显示处同步更新的情况。这是由于js的Event-loop机制;另外,在原生事件中,绕过了React,不会触发isBatchingUpdates变量的改变,所以也会同步进行渲染更新。
(5)React18以后自动批量更新,setState不再是分情况异步了,而是全部异步,但是该条件仅在使用ReactDOM.createRoot(root).render(jsx)时候生效,之前的React.render的API还是支持的,与之前一致
①全自动更新以后,如果想拿到更新之后的数据,一是类组件中通过使用setState(state,callback)的方式,在callback中取到最新的值,函数组件使用useEffect将state作为依赖拿到最新的值
②新的React更新机制进行了变化,不再依赖批量更新的标志,而是根据任务优先级进行更新,优先级高的先执行
4.React中有使用过getDefaultProps吗?它有什么作用?
(1)createReactClass参数对象内的一个方法,用于初始化组件内的属性
5.React中的setState的第二个参数作用是什么?
(1)setState(state,callback);第二个参数是回调函数,通常用于查看更新后的状态,比如更新后的state
6.React中的setState和replaceState的区别是什么?
(1)setState是修改其中的部分状态,相当于Object.assign,只是合并,并不会减少原来的状态
(2)replaceState是完全替换原来的状态,相当于赋值,将原来的state替换为另一个对象,如果新状态属性减少,那么新的state就没有这个状态了
7.在React中组件的this.state和setState有什么区别?
(1)this.state用于获取状态,setState用于更改状态
(2)以下是react文档所说的内容:永远不要this.state直接变异,因为之后调用setState()可能会替换你所做的变异。把this.state看作是不可变的。setState()不会立即改变this.state,但会创建一个挂起状态转换,this.state调用此方法后访问可能会返回现有值,无法保证对setState的调用进行同步操作,并且可以对调用进行批量处理以提高性能。除非实现条件渲染逻辑,否则将始终触发重新渲染shouldComponentUpdate()….
8.state是怎么注入到组件的,从reducer到组件经历了怎样的过程?
(1)通过connect和mapStateToProps将state注入到组件中,mapStateToProps(state,ownProps)两个参数的含义是:
①state-store管理的全局状态对象,所有都组件状态数据都存储在该对象中
②ownProps组件通过props传入的参数
(2)reducer到组件的过程
①reducer对action对象处理,更新组件状态,并将新的状态值返回store。
②通过connect(map.stateToProps,map.DispatchToProps)(Component)对组件Component进行升级,此时将状态值stroe取出并作为props参数传递到组件
9.React组件的state和props有什么区别?
(1)state是用来保存和控制组件自身的状态,它只在constructor中初始化,为组件的私有属性,不可通过外部访问和修改,只能通过组件内的setState()或者useState提供的修改方法修改,改变它会导致组件重新渲染。
(2)props是外部传进组件的参数,主要作用就是父组件向子组件传递数据,由于单向数据流的原因,该数据只读不可变,只能通过外部组件主动修改新传入的props来重新渲染子组件,否则子组件的props以及展现形式不会改变
(3)区别
①props是传递给组件的,而state是在组件内被组件自己管理的
②props是不可修改的,所有react组件都必须像纯函数一样保护他们的props不被更改
③state是在组件中创建的,state是多变的,可修改的,每次setState都是异步更新
10.React中的props为什么是只读的?
(1)单向数据流:数据总是从父组件流向子组件,子组件只能使用,无法更改,也就是纯函数的思想
11.React中组件的props改变时更新组件的有哪些方法
(1)componentWillReceiveProps(已废弃),该生命周期中可以在子组件的render函数执行前,通过this.props获取旧属性,通过nextProps获取新props,对比两次props是否相同,从而更新子组件自己的state
(2)getDerivedStateFromProps,这个生命周期函数为了替代componentWillReactiveProps存在的,所有在需要使用componentWillReceiveProps时,就可以考虑使用getDerivedStateFromProps来进行替代,这个函数不能通过this访问到class属性,也不推荐直接访问属性,而是应该通过参数提供的nextProps以及prevState来进行判断,根据新传入的props来映射到state,如果传入的内容不需要影响到你的state,那么就需要返回一个null,这个返回值是必须的,所以尽量写在函数的末尾
12.React中怎么检验props?验证props的目的是什么?
(1)React提供了PropTypes以供验证使用,当Props传入的数据无效或类型不符时报错
(2)使用TypeScript
(3)目的:尽早发现错误,重构更方便可靠
三、 生命周期
1.React的生命周期有哪些?
(1)挂载阶段,组件被创建,然后组件实例插入到DOM中,完成组件的第一次渲染,该过程只会发生一次,在此阶段会依次调用以下这些方法
①constructor,组件的构造函数,第一个被执行,如果没定义,那么它会执行默认的构造函数,如果定义了,就必须执行super(props),否则无法在构造函数中拿到this,通常在该方法中做两件事,初始化组件的state和给事件处理方法绑定this
②getDerivedStateFromProps,静态方法,不能在函数中使用this,有两个参数props和state,分别指接收到的新参数和当前组件的state对象,返回一个对象用来更新当前的state对象,如果不需要更新则需返回null
③render,必须要有的核心方法,只用来返回需要渲染的内容,通常返回:
1)React元素:这里包括原生dom以及React组件
2)数组和Fragment(片段):可以返回多个元素
3)Portals(插槽):可以将子元素脱离父元素的限制
4)字符串和数字:被渲染成DOM中的text节点
5)布尔值或null:不渲染任何内容
④componentDidMount,组件挂载后,插入DOM树中立即调用,该阶段通常进行以下操作:
1)执行依赖于DOM的操作
2)发送网络请求
3)添加订阅消息(会在componentWillUnmount取消订阅)
4)如果在componentDidMount中调用setState,就会触发一次额外的渲染,多调用了一次render函数,由于他是在浏览器刷新屏幕前执行的,所以虽然用户没有感觉,但是官方不建议我们这样使用
(2)更新阶段,组件状态发生变化,重新更新渲染的过程,当组件的props改变了,或组件内部调用了setState/forceUpdate,会触发更新重新渲染,这个过程可能会发生多次,这个阶段依次会调用下面这些方法
①getDerivedStateFromProps,更新阶段第一个触发,state和props发生变化时触发
②shouldComponentUpdate,是否需要重新渲染,state和props发生变化时触发,通常通过毕竟this.props和nextProps,this.state和nextState值是否变化,来返回true或fasle控制是否继续渲染,当返回false时,组件的更新过程停止,后续生命周期不会调用
③render
④getSnapshotBeforeUpdate,有两个参数prevProps和prevState,表示更新之前的props和state,这个函数必须要和componentDidUpdate一起使用,并且要有一个返回值,默认是null,这个返回值作为第三个参数传给componentDidUpdate
⑤componentDidUpdate,当组件更新后,对DOM进行操作,也可在此进行网络请求,该方法有三个参数:
1)prevProps:更新前的props
2)prevState:更新前的state
3)snapshot:getSnapshotBeforeUpdate()生命周期的返回值
(3)卸载过程,组件从DOM树中被移除的过程
①componentWillUnmount(),在组件卸载及销毁之前调用,在此方法中执行必要的清理操作:
1)清除timer,取消网络请求或清除
2)取消在componentDidMount()中创建的订阅等
(4)错误处理阶段
①componentDidCatch(error,info),此生命周期在后代组件抛出错误后被调用,接收两个参数,error:抛出的错误,info:带有componentStack key的对象,其中包含有关组件引发错误的栈信息
2.React废弃了哪些生命周期?为什么?
(1)componentWillMount,这个函数的功能完全被componentDidMount和constructor来替代,异步获取数据或者初始化数据都被两者替代
(2)componentWillReceiveProps,在老版本的react中,如果组件自身的某个state跟其props密切相关的话,一没有一种很优雅的处理方式去更新state,而是需要在componentWillReceiveProps中判断前后两个props是否相同,如果不同再将新的props更新到相应的state上去,这样做一来会破坏state数据的单一数据源,导致组件的状态变得不可预测,另一方面也会增加组件的重绘次数。
(3)componentWillUpdate,与componentWillReceiveProps类似,它们都有可能在一次更新中调用多次,也就是说在这里的回调函数也有可能会被调用多次。与componentDidMount类似,componentDidUpdate不存在这样的问题,一次更新只会调用一次
(4)getSnapshotBeforeUpdate(prevProps,prevState),返回的值作为componentDidUpdate的第三个参数,与willMount不同的是,getSnapshotBeforeUpdate会在最终确定的render执行之前,也就是能保证其获取到的元素状态与didUpdate中获取到的元素状态相同
3.React 16.x中props改变后在哪个生命周期中处理?
(1)在getDerivedStateFromProps中处理,注意如果props传入的内容不影响你的state,那么就需要返回一个null,这个返回值是必须的,所以尽量将其写到函数的末尾
4.React性能优化在哪个生命周期?它优化的原理是什么?
(1)shouldComponentUpdate,通过返回值控制是否需要重新渲染
(2)useCallback和memo也可也给函数组件赋予控制是否重新渲染的功能
5.state和props触发更新的生命周期分别有什么区别
(1)相比state更新,props更新后唯一的区别是增加了对cpmponentWillReceiveProps的调用,关于该函数,它在component接受到新的props时被触发,接收一个nextProps的参数(对应新的props值),它被废弃前,可以用来毕竟this.props和nextProps来重新setState,React 16中用getDerivedStateFromProps代替
6.React中发起网络请求应该在哪个生命周期中进行?为什么
(1)异步请求,官方推荐放在componentDidMount中去操作,对于同步的状态改变,可以放在componentWillMount中,一般用的比较少
(2)componentDidMount调用时,DOM已经挂载,所以可以保证数据的加载,调用setState时,会触发重新渲染,所以官方设计这个方法就是用来加载外部数据或处理其他副作用的。
7.React16中新生命周期有哪些?
(1)getDerivedStateFromProps,静态方法,可以根据props派生state,无法获取this
(2)getSnapshotBeforeUpdate,参数为更新后的props和state,返回值为任意内容,将作为componentDidUpdate的第三个参数,在最近一次渲染输出之前调用,它能从组件在发生更改之前从DOM中捕获一些信息,如滚动位置
(3)componentDidCatch,参数为错误对象error和info带有componentStack key的对象,其中包含有关组件引发的错误栈信息,在提交阶段调用,DOM已更新,抓取UI错误,不会导致整个应用崩溃,而可以进行错误处理,并进行优雅降级,渲染备用的UI错误边界,不过无法捕获以下场景中产生的错误:
①事件处理
②异步代码(例如setTimeout或requestAnimationFrame回调函数)
③服务端渲染
④组件自身抛出的错误而非其子组件
四、 组件通讯
1.父子组件的通信方式
(1)父子通信
①props,父组件通过props传递参数给子组件
②React.createRef()创建ref调用子组件的方法传递参数
(2)子父通信
①回调函数,子组件通过调用父组件的方法传递参数
②事件冒泡,父组件会接收到子组件的事件捕获,从而触发父组件的方法
2.跨级组件的通信方式?
(1)props,层层传递
(2)context,在组件嵌套的外层设置状态仓库,通过访问同一个仓库获取数据
(3)第三方库pubsub-js实现消息的订阅与发布
(4)redux全局状态管理
3.非嵌套关系组件的通信方式
(1)第三方库pubsub-js(发布订阅模式)
(2)通过redux进行全局状态管理
4.如何解决props层级过深的问题
(1)Context API:提供一种组件之间的状态共享,而不必通过显示组件树逐层传递props
(2)redux全局状态库
5.组件的通信方式有哪些
(1)父子通信
(2)子父通信
(3)跨层级通信
(4)全局状态管理
(5)发布订阅模式
五、 虚拟DOM
1.对虚拟DOM的理解?虚拟DOM主要做了什么?虚拟DOM本身是什么?
(1)从本质上说,虚拟DOM是一个js对象,通过对象的方式来表示DOM结构,将页面状态抽象为js对象的形式,配合不同的渲染工具,使跨平台渲染成为可能,通过事务处理机制,将多次DOM修改的结果一次性更新到页面上,从而有效的减少页面渲染的次数,减少修改DOM的重绘重排次数,提高渲染性能。
(2)虚拟DOM是对DOM的抽象,这个对象是更加轻量级对DOM的描述,设计之初就是为了更好的跨平台,因为虚拟dom本身就是js对象,在代码渲染到页面之前,vue或react会把代码转换成一个对象(虚拟DOM),以对象的形式来描述真实dom结构,最终渲染到页面,每次数据发生变化前,虚拟DOM都会缓存一份,变化之时会将现在的虚拟dom与缓存的虚拟dom比较,来重新渲染更改的部分
(3)为什么使用虚拟DOM?
①尤雨溪说过,框架给你的保证是你不需要手动优化的情况下,给你提供过得去的性能
②跨平台,更方便的跨平台操作,比如服务端渲染,uniapp等
2.React diff算法的原理是什么?
(1)通过比较虚拟DOM树发生变化以后,将更新补丁作用于真实DOM,以最小成本完成视图更新
(2)具体流程是
①真实DOM首先会映射为虚拟DOM
②当虚拟DOM发生变化后,根据差距计算生成patch,这个patch是一个结构化的数据,内容包含了增加,更新,移除等
③根据patch去更新真实DOM,反馈到用户界面上
(3)总结为三个策略,分别从树、组件及元素三个层面进行复杂度的优化;
①策略一:忽略节点跨层级操作场景,提升比对效率(基于树进行对比);对同一层级的节点进行比较,如果发现节点已经不存在了,则该节点及其子节点会被完全删除掉,不会进一步的比较,这就提升了对比效率
②策略二:如果组件的class一致,则默认为相似的树结构,否则默认为不同的树结构(基于组件进行对比);对比过程中,如果组件是同一类型则进行树比对,如果不是,则放入补丁中,只要父组件类型不同,就会被重新渲染,这也就是为什么shouldComponentUpdate、PureComponent及React.memo能提高性能的原因
③策略三:同一层级的子节点,可以通过标记key的方式进行列表对比(基于节点进行对比);元素比对主要发生在同层级中,通过标记节点操作生成补丁,节点操作包含了插入、移动、删除等。其中节点重新排序同时涉及插入、移动、删除三个操作,所以效率消耗最大,此时策略三起到了至关重要的作用,通过标记key的方式,React直接移动DOM节点,降低内耗
3.React key是干嘛用的?为什么要加?key主要是解决哪一类问题的?
(1)keys是React用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。在开发过程中,我们要保证某个元素的key在同级元素中据有唯一性。
(2)在Diff算法中React会借助元素的key值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重新渲染,此外,React还需要借助Key值来判断元素与本地状态的关联关系
(3)注意事项:
①key值一定要和具体的元素一一对应
②尽量不要用数组的index作为key
③不要在render的时候用随机数或者其他操作给元素加上不稳定的key,这样造成的性能开销比不加key更糟糕
4.虚拟DOM的引入与直接操作原生DOM相比,哪一个效率更高?为什么?
(1)如果只修改一个按钮的文案,那么虚拟DOM操作无论如何都不可能比真实DOM操作更快。在首次渲染大量DOM时,由于多了层虚拟DOM的计算,虚拟DOM也会比innderHtml插入慢,但是它能保证性能下限,对于真实DOM操作的时候进行针对性优化时,还是更快的
5.React与Vue的diff算法有何不同?
(1)React的diff算法,触发更新呃时机主要再state变化与hooks调用后,此时触发虚拟dom树变更遍历,采用了深度优先遍历算法。但传统的遍历方式,效率较低。为了优化效率,使用了分治的方式,将单一节点比对转为了三种类型节点的比对,分别是树、组件及元素,以此提升效率
(2)树比对:DOM树同层级节点比对
(3)组件比对:如果组件是同一类型,则进行树比对,如果不是,则直接放入补丁中
(4)元素比对:主要发生在同层级中,通过标记节点操作生成补丁,节点操作对应真实DOM剪裁操作
(5)React16起,引入了Fiber架构,为了使整个更新过程可随时暂停恢复,节点与树分别采用了FiberNode与FiberTree进行重构。FiberNode使用了双链表的结构,可以直接找到兄弟节点与子节点,整个更新过程由current与workInProgress两株树双缓冲完成,workInProgress更新完成后,再通过修改current相关指针指向新节点
(6)vue的diff算法对新老VDOM进行对比时,是从节点的两侧向中间对比,如果节点的key值与元素类型相同,属性值不同,就会认为是不同节点,就会删除重建
(7)react的diff算法对新老VDOM进行对比时,是从节点的左侧开始对比,好比将新老VDOM放入两个栈中,一对多依次对比,如果节点的key值与元素类型相同,属性值不同,react会认为是同类型节点,只修改节点属性
(8)相同点:都只进行同级比较,忽略了跨级操作,常见现象就是对数组或对象的深层元素进行修改时,视图不会更新
六、 Hooks
1.对React HOOK的理解,它的实现原理是什么?
(1)函数组件最早是无状态组件,用来渲染简单组件
(2)相比类组件,函数组件肉眼可见的特质自然包括轻量,灵活,易于组织和维护,较低的学习成本等,对比起来,两种组件
①类组件需要继承class,函数组件不需要
②类组件可以访问生命周期方法,函数组件不能
③类组件中可以获取实例化后的this,并基于this实现各种各样的事情,而函数组件不可以
④类组件可以定义并维护自身的state,函数组件不可以
⑤React组件本身的定位就是函数,一个输入数据,输出UI的函数,开发者编写的声明式的代码,React的主要工作是及时地把声明式代码转换为命令式的DOM操作,把数据层面的描述映射到用户可见的UI变化中,这就意味着从原则上讲,React的数据应该总是紧紧的和渲染绑定在一起,而类组件不行,函数组件更加撇撇其设计理念,也更有利于逻辑拆分与重用的组件表达形式
2.为什么useState要使用数组而不是对象
(1)数组在ES6中解构出来命名是什么,值就是什么
(2)对象在ES6中解构,必须要和useState内部实现返回的对象同名,想要使用多次的话,必须设置别名才能使用返回值
(3)总结:useState返回数组是为了降低使用的复杂度,返回数组还可以按顺序解构,返回对象的话想多次使用就需要定义别名
3.React Hooks解决了哪些问题?
(1)在组件之间复用逻辑很难,hook使我们在无需修改组件结构的情况下复用状态逻辑
(2)复杂组件变得难以理解,类组件中会在各个生命周期函数中处理状态,hook把组件中相关联的部分拆解,不强制按照生命周期划分,还可以使用reducer来管理组件内状态,使其更加可预测
(3)难以理解的class,什么时候使用类组件与函数组件让人难以决策,hook让你在非class的情况下可以使用更多的React特性,从概念上讲,React组件一直更像是函数,而hook则拥抱了函数,同时也没有牺牲React的精神原则
4.React Hook的使用限制有哪些?
(1)不要在循环,条件或嵌套函数中调用Hook;
(2)只能在函数组件中使用hook
5.useEffect与useLayoutEffect的区别?
(1)共同点:
①都是处理副作用,这些副作用包括改变DOM,设置订阅,操作定时器
②使用方式完全一致
(2)不同点:
①使用场景:useEffect在react的渲染过程中是被异步调用的,用于绝大多少场景,而useLayoutEffect会在所有DOM变更之后同步调用,主要用于处理DOM操作,调整样式,避免页面闪烁等问题。也正是因为同步处理,所以需要避免在useLayoutEffect做计算量较大的耗时任务从而造成阻塞
②使用效果:useEffect是按照顺序执行代码的,改变屏幕像素之后执行,当改变屏幕内容时可能会产生闪烁;useLayoutEffect是改变屏幕像素之前就执行了,推迟页面显示,先改变DOM再渲染,不会产生闪烁,useLayoutEffect总是比useEffect先执行
6.React Hooks在平时开发中需要主要的问题和原因
(1)不要在循环,条件或嵌套函数中调用hook,必须始终在React顶层使用Hook
(2)使用useState时候,使用push、pop、splice等直接修改数组对象的坑
(3)useState设置状态的时候,只有第一层生效,后期需要更新状态,必须通过useEffect
(4)善用useCallback,父组件给子组件传参时,即使没有任何变动,每次父组件渲染子组件也会跟着渲染一次
(5)不要滥用useContext,可以使用基于useContext封装的状态管理工具
7.React Hook和生命周期的关系?
(1)函数组件的本质是函数,没有状态state,因此也没有生命周期,它仅仅是类组件的render函数,使用hook了以后,按照执行顺序,函数组件也就有了生命周期
(2)useState初始化state,类似constructor
(3)React.memo包裹的组件,类似shouldComponentUpdate和PureComponent,浅比较判断是否需要重新渲染
(4)useEffect,类似componentDidMount,componentDidUpdate,区别在于第二个数组参数中的值,如果useEffect中返回了一个函数,那这个返回函数就类似componentWillUnmount卸载函数
七、 Redux
1.对Redux的理解,主要解决什么问题?
(1)React是视图层框架,相当于View,Redux是一个用来管理数据状态和UI状态的js应用工具
(2)Redux提供了一个store仓库,组件通过dispatch将需要传递的state传入store,不需要通过其他组件
(3)解决了跨层级组件通信问题,通过对象驱动组件进入生命周期,方便数据管理和切片
2.Redux原理及工作流程
(1)Redux源码主要分为以下几个模块文件
①compose.js提供从右到左进行函数式编程
②createStore.js提供作为生成唯一store的函数
③combineReducers.js提供合并多个reducer的函数,保证store的唯一性
④bindActionCreators.js可以让开发者在不直接接触dispacth的前提下进行更改state的操作
⑤applyMinddleware.js通过中间件来增强dispatch的功能
(2)工作流程
①createStore创建仓库,生成数据
②action:{type:Symble(),payload:”payload”}定义行为
③dispatch发起action:store.dispatch()
④reducer:处理action,返回新的state
⑤总结:用户通过view发出action,发出方式就用到了dispatch方法,然后store自动调用Reducer,并传入当前state和收到的action,Reducer会返回新的state,state变化,触发更新
3.Redux中异步的请求怎么处理?
(1)通常借助redux的异步中间件来进行异步处理,当下主流的异步中间件有两种,redux-thunk和redux-saga
(2)redux-thunk
①优点:体积小,使用简单
②缺陷:样板代码过多,耦合严重,功能孱弱,实际开发中某些情况需要自己封装
(3)redux-saga
①优点:
②异步解耦,异步操作被转移到单独saga.js中;
③action不像redux-thunk混杂;
④异常处理:代码异常/请求失败都可以通过try/catch语法捕获处理
⑤功能强大:redux-saga提供了大量的saga辅助函数和Effect创建器可以使用
⑥灵魂:redux-saga可以将多个saga可以串行/并行组合起来,形成非常实用的异步flow
⑦易测试:提供了各种case的测试方案,包括mock,分支覆盖等
⑧缺陷:额外的学习成本,体积庞大,功能过剩,ts支持不友好
4.Redux怎么实现属性传递,介绍下原理?
(1)react-redux数据传递:view->action->reducer->store->view
(2)例如点击事件传递
①view上的onClick事件通过mapDispatchToProps把数据传到action->click:()=>dispatch(add)
②action的add传到reducer上
③reducer传到store上 const store = createStore(reducer);
④sotre再通过mapStateToProps映射到view上text
5.Redux中间件是什么?接受几个参数?柯里化函数两端的参数具体是什么?
(1)Redux中间件提供的是位于action被发起后,到达reducer之前的扩展点,换而言之,原本view->action->reducer->store的数据流加上中间件后变成了view->action->middleware->reducer->store,在这一环节可以做一些副作用的操作,如异步请求,打印日志等
(2)redux中间件接受一个对象作为参数,对象上的参数有两个字段dispatch和getState,分别代表Redux Store上的两个同名函数
(3)柯里化函数两端一个是middewares,一个是store.dispatch
6.Redux请求中间件如何处理并发
(1)redux-saga是一个管理redux异步操作的中间件,用来代替redux-thunk的,它通过创建sagas将所有的异步操作逻辑存放在一个地方集中处理,以此将react中的同步操作与异步操作区分开,便于维护和管理
(2)takeEvery可以让多个saga任务并行被fork执行
(3)takeLatest不允许多个saga任务并行地执行,一旦接收到新发起的action,他就会取消前面所有fork过的任务(如果这些任务还在执行的话),在处理ajax请求的时候,如果只希望获取最后那个请求的响应,takeLatest就会非常有用
7.Redux状态管理器和变量挂载到window中有什么区别?
(1)两者都是存储数据已供后期使用,但是Redux状态更改可回溯,数据多了以后可以很清晰的知道改动在哪里发生,并且有一套完整的状态管理模式
8.mobox和redux有什么区别?
(1)共同点
①两者都是为了解决状态不好管理,无法有效同步的问题而产生的工具
②都是用来统一管理应用状态的工具
③某一个状态只有一个可靠的数据来源
④操作更新的方式都是统一的,并且是可控的
⑤都支持store与react组件,如react-redux,mobx-react
⑥Redux是一个js库,通过action(一个对象,包含type和payload属性)中的type判断需要处理的数据是什么,通过payload进行数据负载,Reducer是一个纯函数,用来通过对用每一个action中的type去进行对应的store中的数据进行操作。store提供三个功能,1.getstate()获取数据,2.dispatch(action)监听action的分发进行数据更新,3.支持订阅store的变更当数据,总而言之,当组件中使用store,可以通过getstate获取到数据,通过dispatch(action)进行数据的更新,通过subscribe监听到数据,当对应的store中的数据被修改时,组件也重新渲染
⑦Mobx,是一个透明函数响应式编程的状态管理库,它使得状态管理简单可压缩,-Mobx在aciton中定义改变状态的动作函数,包括如何变更状态。-Mobx在store中集中管理状态state和动作aciton
⑧总结:
1)redux将数据保存在单一的store中,而mobx将数据保存在分散的多个store中
2)redux使用plain object保存数据,需要手动处理变化后的操作,mobx使用observable保存数据,数据变化后自动处理响应的操作
3)redux使用的是不可变状态,意味着状态只是只读的,不能直接去修改它,而是应该返回一个新的状态,同时使用纯函数;mobx中的状态是可变的,可以直接对其进行修改
4)mobx相对来说比较简单,在其中有很多抽象,mobx使用的更多的是面向对象思维,redux会比较复杂,因为其中的函数式编程思想掌握起来不是那么容易,同时需要借助一系列的中间件来处理异步和副作用
5)mobx中有更多的抽象和封装,所以调用起来会更加复杂,同时也让结果难以预测,而redux提供可以进行时间回溯的开发工具,同时其纯函数以及更少的抽象,让调试变得更加容易
9.Redux和Vuex有什么区别,它们的共同思想?
(1)Vuex改进了Redux中的Action和Reducer函数,以mutations变化函数取代Reducer,无需switch,只需要在对应的mutation函数里改变state值即可
(2)Vuex由于Vue自动重新渲染的特性,无需订阅重新渲染函数,只要生成新的state即可
(3)Vuex数据流的顺序是:View调用store,commit提交对应的请求到Store中对应的mutation函数来处理store改变
(4)vuex弱化dispatch,通过commit进行store状态改变,取消了action概念,不必传入特定的action形式进行指定变更,弱化reducer,基于commit参数直接对数据进行转变,使框架更加简易
(5)共同思想:单一数据源,变化可以预测
10.Redux中间件是怎么拿到store和action?然后怎么处理?
(1)redux中间件本质就是一个函数柯里化,redux applyMiddleware api源码中每个middleware接受两个参数,store的getState函数和dispatch函数分别获得store和action,最终返回一个函数,该函数会被传入next的下一个middleware的dispatch方法,并返回一个接收action的新函数,这个函数可以直接调用next(action),或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个middleware会接受真实的stroe的dispatch方法作为next参数,并借此结束调用链。所以middleware的函数签名是({getState,dispatch})=>next=>action
11.Redux中的connect有什么作用?
(1)负责连接React和Redux
(2)获取state,connect通过context获取Provider中的store,通过store.getState()获取整个store tree上所有state
(3)包装原组件,将state和action通过props的方式传入到原组件内部,wrapWithConnect返回一个ReactComponent对象Connect,Connect重新render外部传入的原组件WrappedComponent,并把connect中传入的mapStateToProps,mapDispatchToProps与组件上原有的props合并后,通过属性的方式传给WrappedComponent
(4)监听store tree变化
(5)connect 缓存了store tree中state的状态,通过当前state状态和变更前state状态进行比较,从而确定是否调用this.setState()方法触发Connect及其子组件的重新渲染
八、 路由
1.React-Router的实现原理是什么?
(1)基于hash的路由:通过监听hashchange事件,感知hash变化,改变hash可以通过locahion.hash=xx
(2)基于history路由:改变url可以通过history.pushState和resplaceState等,会将URL压入堆栈,同时能有应用history.go()等api,监听url的变化可以通过自定义事件触发实现
(3)思想:基于history库来实现上述不同路由模式,并且能够保存历史记录等,磨平浏览器差异,上层无感知,通过维护的列表,每次url变化的回收,通过配置的路由路径匹配相应的Component,并且render
2.如何配置React-Router实现路由切换
(1)通过react-router-dom的
(2)v6版本的react-router-dom,
(3)使用useRoutes和Navigate实现路由表和重定向
3.React-Router怎么设置重定向?
(1)v6以前用
(2)v6以后用
4.react-router里的Link标签和a标签的区别
(1)从最终渲染的DOM来看,都是链接,都是标签
(2)区别是react-router接管了其默认的链接跳转行为,区别于传统的页面跳转,link标签的“跳转”行为只会触发相匹配的对应页面的内容跟新,而不会刷新整个页面
5.React-Router如何获取URL的参数和历史对象?
(1)通过params
①类组件:在保证props能够获取到路由信息的前提下,需要使用withRouter的HOC,通过this.props.match.params获取
②函数组件通过useParams
(2)历史对象
①类组件:this.props.history
②函数组件通过useHistory
6.React-Router4怎样在路由变化时重新渲染同一个组件?
(1)利用componentWillReceiveProps生命周期进行重新render的预处理操作
(2)或者给两个相同的route设置不同的key
7.React-Router的路由有几种模式?
(1)支持hash(对应HashRouter)和browser(对应BrowserRouter)两种路由规则
(2)react-router-dom提供了BrowserRouter和HashRouter两个组件对应两种模式
(3)BrowserRouter模式对应URL:http://xxxx.com/path
(4)HashRouter模式对应URL:http://xxxx.com/#/path
8.React-Router 4的Switch有什么用?
(1)用来包裹
九、 其他
1.React组件命名推荐的方式是那个?
(1)通过引用export default而不是使用“displayName”来命名组件
2.React最新版本解决了什么问题,增加了哪些东西?
(1)最近的React18新增了4个新特性
①Automatic batching
1)react中使用setState来进行dispatch组件的state变化,当setState在组件中被调用后,并不会立即触发重新渲染,React会执行全部事件处理函数,然后触发一个单独的re-render合并所有更新。
2)比如在点击+1的例子中,如果方法里连续触发三次setState,最终React会将更新函数放在一个队列里,然后合并队列触发setState的re-render,这就是batching的含义,在React18中,任何情况下都可以合并渲染,如果仍然希望setState之后立即渲染,只需要用flushSync包裹
②Concurrent APIs
1)官方明确指出了React18中并不存在Concurrent Mode,只有用于并发渲染的并发新特性,开发者希望能够在Web Platform引入并发渲染来实现多个渲染任务并行渲染,其中Suspense就是基于此诞生的
2)React18支持并发特性的三个API:startTransition():被startTransition包裹的setState触发的渲染被标记为不紧急渲染,意味着他们可以被其他紧急渲染所抢占,这种渲染优先级的调整手段可以帮助我们解决各种性能伪瓶颈、useDeferredValue():这个hook适用于设置延迟值,例如搜索同时列表快速随着输入更新,该hook会将这个业务场景下的list渲染变得更加平滑,深层次来看是defered value引起的渲染会被标记为不紧急渲染,会被其他渲染抢占、useTransition()
③SSR for Suspense
1)React18中,Suspense可以运行在服务端,Server Rendering的性能不再受限制与性能最差的组件,react18以前,server rendering的流程就是服务端请求所有数据,然后发送html到客户端或者浏览器,然后由客户端的hydrate内容,每个环节必须按部就班的执行。当Suspense可以在服务端使用之后,一旦某个组件加载慢,就可以将fallback的内容传输到客户端(例如loading态),保证用户尽早的可进行交互。更优秀的是,hydrate可以通过用户行为调整优先级,例如两个组件同时处于Suspense的流程中,此时用户点击某个组件,react会优先该组件,尽可能满足优先满足用户交互体验
④New Render API
1)新的更友好的语义化render方式,旧:ReactDOM.render(
2)客户端提供了新的api,以及新的useId()来为组件生成唯一ID,useSyncExternalStore这个hook来保证External stores的一致性
3.react实现一个全局的dialog
(1)…….
4.React 数据持久化有什么实践吗?
(1)React项目中,通过redux存储全局数据时,用户刷新网页,redux存储的全局数据就会全部清空,通常不使用第三方库的情况下,考虑存一份到localStorage上,如果使用第三方库
(2)redux-persist ;用法上,只需要修改store的生成代码,在index.js中,将PersistGate标签作为网页内容的父标签
5.对React和Vue的理解,他们的异同
(1)Templating 和jsx;react是通过js生成html,通过js操作css,通过js来控制html,列表渲染条件判断等,vue是把html,css,js组合到一起,用各自的处理方式,模板语法,简化了渲染逻辑
(2)单向绑定和双向绑定
①单双向绑定指的是view层和model层之间的映射关系,双向绑定是在同一个组件内,将数据和视图绑定起来
②react是用户访问view,用户发出交互到Action中进行处理,Action中通过setState对state进行更新,view层不能直接修改state,必须通过action来进行操作
③vue即支持插槽式{{}},v-bind单项绑定,也支持表单的v-model(实际是v-bind和input事件的组合)
(3)框架设计模式不同
①Vue是MVVM模式的一种方式,但是没完全遵循MVVM模型
②React是前端组件化框架,只是View层,MVC模式
(4)状态管理
①React中state是不可变的,需要使用setState()方法更新状态,vue中state对象不是必须的,可以直接修改
(5)性能优化
①在react中,当组件状态改变时(setStae后),他会触发整个子组件树重新渲染,以根组件作为渲染基点,必须使用PureComponent或者用shouldComponentUpdate
②vue中,vue组件在初始化时会通过Object.defineProperty对每个date建立对应的wather,然后在模板编译时收集依赖,只要修改date的任意属性,就会精准的触发该视图的重新渲染,通过getter/setter以及一些函数的劫持
③react的性能优化需要手动去做,vue的性能优化是自动的
(6)虚拟DOM,各自的diff算法有较大差别
(7)相同点:单向数据流(组件之间的数据流动都是单向数据流),都使用虚拟DOM,比较新旧虚拟DOM的差异,更新视图,组件化
6.可以使用TypeScript写React应用吗?怎么操作?
(1)创建项目时,用create-react-app 项目名 —template typescript
(2)现有项目引入ts,npm install —save typescript @types/node @types/react @types/react-dom @types/jest,然后将jsx或者js文件后缀名改成tsx,重启项目
7.React设计思路,它的理念是什么?
(1)编写简单直观的代码,声明式的直观的编码方式
(2)简单可复用的组件,贯彻组件化的概念,每个功能模块都定义成组件,通过组合嵌套的方式构成更大的组件
(3)虚拟DOM,React把真实DOM换成js对象树,对比新旧虚拟dom,一次性更新,减少dom操作
(4)函数式编程
8.React中props.children和React.Children的区别
(1)React中,涉及组件嵌套时,在父组件中使用props.chlidren把所有子组件显示出来,如果想把父组件中的属性传给所有子组件,就需要使用React.Children方法。
(2)this.props.children代表组件的所有子节点
9.React的状态提升是什么?使用场景有哪些?
(1)用户对子组件组件的操作,子组件不改变自己的状态,通过自己的props把这个操作改变的数据传递给父组件,改变父组件的状态,从而改变受父组件控制的所有子组件的状态。
(2)官方原话是:共享state(状态)是通过将其移动到其他需要它的组件的最接近的共同祖先组件来实现的。这被称为“状态提升”
10.React中的constructor和getInitialState的区别?
(1)一个是用在class组件中,一个是createClass创建的组件中使用
(2)ES6语法
①用class声明一个类组件,且要继承react组件的方法和属性的时候,使用者可以直接指定this.state={},要修改时直接使用this.setState()来改变组件状态
(3)ES5语法
①如果使用createClass方法创建一个Component组件,可以自动调用它的getInitialState方法来获取初始化的state对象,但是在ES6的class就不会自动调用,因此es5要把状态return出去
11.React的严格模式如何使用,有什么用处?
(1)使用时在组件外包裹
(2)好处在于:
①识别不安全的声明周期
②关于使用过时字符串refAPI的警告
③关于使用废弃的findDOMNode方法的警告
④检测意外的副作用
⑤检测过时的contextAPI
12.在React中遍历的方法有哪些?
(1)React.Children.map和React.Children.forEach这两个方法。他们的参数都是在组件中接受props.children这个ReactNode作为参数,然后进行遍历。专门提供这两个方法的目的是props.children可能是字符串、null、数组,用React.Childrem.map可以抹平这些数据类型的差异,使之都能进行循环,并返回合理的值;React.Children.map有返回值(当前组件被遍历的数组,注意React.Fragment不会被遍历),React.Children.forEach没有返回值
13.React中页面重新加载时怎样保留数据?
(1)如果不考虑第三方库的情况下,使用浏览器localstorage来保存应用程序状态
(2)第三方插件redux-persist ;
14.同时引用这三个库react.js、react-dom.js和babel.js它们都有什么作用?
(1)React.js:React中的组件(Component)、Context、hooks等核心Api还有虚拟DOMde的比较,Fiber的算法实现等
(2)React-dom.js与web浏览器DOM相关的API,比如虚拟DOM的挂载,DOM的更新,Portal等
(3)babel.js;ES6+代码的转义
15.React必须使用jsx吗?
(1)不是必须使用,jsx只是一个js的语法扩展,本身没有太多定义,也没有更多标准
(2)在没有jsx的时候,react实现一个组件依赖于React.createElement()函数,但是jsx有以下优点:
①执行更快,因为他在编译为js代码后进行了优化
②类型安全,编译过程中就能发现错误
③声明式语法更加直观,与html结构相同
(3)jsx更像一种语法糖,通过类似XML的描述方式,描写函数对象
(4)babel插件会读取代码并解析,生成AST,再将AST传入插入层进行转换,在转换过程中就可以将jsx的结构转为React.createElement函数
16.为什么使用jsx的组件中没有看到使用react确需要引入react?
(1)因为组件的渲染,jsx的编译依赖react.createElement()函数,所以必须引入react
17.在React中怎么使用async/await?
(1)fn1 = async ()=>{ await fn2() };
18.React.Children.map和js的map有什么区别?
(1)React.Children.map能处理未知数据类型,即使React.children是null或者undefined也能正确处理
(2)js的map只能处理数组
19.对React SSR的理解
(1)SSR指的是服务端渲染,客户端渲染加载的是模板页面,通过请求来的数据填充模板得到最终页面,服务端渲染加载的是完整页面,服务器提前把数据填充好,直接返回html
(2)优点:更好的SEO(搜索引擎优化),首屏加载速度更快
(3)缺点:开发条件会受限制,当需要外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于node.js运行环境
(4)相比客户端渲染带来更多的服务器负载
20.为什么React要使用jsx?
(1)React并没有采用将标记与逻辑分离不同文件这种人为的分离方式,而是将二者共同存放在“组件”的松散耦合单元中,来实现关注点分离
(2)React不强制要求使用jsx,但大多数人发现在JS代码中,将jsx和UI放在一起,视觉上有辅助作用。它还可以使React显示更多有用的错误和警告信息
21.HOC(高阶组件)相比mixins有什么优点?
(1)早期react也是用的mixins的方式,但是在使用class的方式创建组件以后,mixins的方式就不能用了,并且其实mixins也是存在一些问题的,比如:隐含一些依赖,多个mixin中可能存在相同命名的函数,一个mixin被多个组件使用,可能会存在需求使得mixin修改原本的函数或新增更多函数,这就产生了维护成本
(2)高阶组件既不会修改原组件,也不会复制组件的行为,相反高阶组件是通过将原组件包裹在容器组件的方式来使用原组件。高阶组件就是一个没有副作用的纯函数
22.React中的高阶组件运用了什么设计模式?
(1)装饰器模式。即允许向一个现有对象添加新的功能,同时又不改变其结构,属于包装模式的一种