React面试题(1)(穿插js基础)

React面试题整理

最近在看React相关的面试题,对相关的面试题整理了一下,作为平时学习的记录

一、React组件的构造函数

(1)constructor必须用super()初始化this,可以绑定this到事件上
(2)如果在constructor中使用this.props,就必须在super中传入props参数,super(props)
(3)无论有没有constructor,在render或其他生命周期函数中都可以使用this.props,因为是默认自带的
(4)如果组件中没有声明constructor,React会默认添加一个空的constructor
(5)ES6中采用的是先创建父类的实例this,因此要先调用super方法,然后再用子类的构造函数去修改this


二、super()和super(props)的区别

(1) constructor()构造方法
  这是ES6对类的默认方法,通过new命令生成对象实例时自动调用该方法,而且该方法是类中必须有的。如果没有显式定义,则会默认添加空的 constructor
 
(2)super()继承
  在class方法中,继承是使用extends关键字实现的。子类必须在constructor()调用super()方法,否则新建实例会报错,这是因为子类没有自己的this对象,它只能继承父类的this对象,而super()就是将父类中的this对象继承给子类。所以没有super子类就没有this对象

(3)ES5和ES6关于继承的实现的不同之处
在ES5中new constructor(),这个过程发生了一下四件事情:
①.生成一个空对象,并将其作为this
②.将空对象的__proto__指向构造函数的prototype
③.运行构造函数
④.如果构造函数没有return或者return返回的是基本数据类型,则返回this,如果返回的是一个引用类型,则返回这个引用类型的对象
因此,在ES5的继承中,是先创建子类的实例对象,然后再见父类的方法添加到this上,即parentObject.apply(this)。而ES6采用的是先创建父类的实例this对象,因此要先调用super()方法,然后再用子类的构造函数去修改this

(4)super(props)和super()以及不写super的区别
如果用到constructor就必须写super(),是用来初始化this的,可以绑定this到事件上
如果要在constructor中使用this.props,就必须给super加props
如果没有用到constructor,可以不写,React会默认添加一个空的constructor
(无论有没有constructor,在render中this.props都是可以使用的,这是React自动附带的)


三、React的渲染机制

(1)React整个的渲染机制就是React会调用render()函数构建一棵DOM树
(2)在state/props变更的时候。render()函数会被再次调用渲染出另外一棵树,重新渲染出所有的节点,构建一棵虚拟的DOM树,用diff算法跟原来的DOM进行对比,找到需要更新和修改的地方进行批量改动,这样就减少了对DOM的频繁操作,节省了开销,提升了性能。
延伸:
URL从输入到页面展示这个过程发生了什么?
在这里插入图片描述


浏览器渲染HTML的步骤
(1)HTML被HTML解析器解析成DOM树,CSS被CSS解析器解析成CSSOM树

(2)DOM树和CSSOM树解析完成后,被附加到一起,形成渲染树(Render Tree)

(3)节点信息计算(重排),这个过程叫做Layout(WebKit)或Reflow(Mozilla)。根据渲染树计算每个节点的几何信息

(4)渲染绘制(重绘),这个过程叫做Painting或Repaint,也就是根据计算好的信息绘制页面

所以,每一次的DOM更改或者CSS几何属性的更改,都会引起一次浏览器的重排/重绘,而如果只是CSS的非几何属性的更改,则只会引起重绘。因此,重排一定会引起重绘,而重绘不一定会引起重排

重绘和重排

重排:

浏览器渲染页面默认采用的是流式布局。重排就是根据渲染树中的每个渲染对象的信息,计算出每个渲染的几何信息(DOM对象的位置和大小),将其布局到页面中的对应位置。

当某个DOM节点的信息发生改变的时候,浏览器就需要对DOM进行重新计算,对页面进行重新布局,引起回流。

引起重排的常见操作:

① 页面的首次渲染

② 浏览器窗口大小发生改变

③元素尺寸或者大小发生改变

④元素内容发生变化(比如:图片的长宽,文字的数量等)

⑤元素的字体大小发生变化

⑥添加或者删除可见的元素

⑦设置style属性

⑧激活CSS伪类

⑨查询某些属性或调用某些方法

重绘:

当页面中的元素样式改变但不影响元素的位置或者大小的时候,比如更改字体的颜色,改变某个元素的背景颜色。


前端性能优化

(1)减少DOM操作

(2)减少重排

(3)css

(4)图片懒加载

(5)使用事件委托

(6)防抖节流

(7)CDN加速


四、为什么React中的props是只读的

因为在React16中props的属性描述符中,属性configurable默认值为false,因此,props既不能修改也不能删除,仅是只读
这也符合React中父组件数据自上而下单向流向子组件,在子组件中不能直接去修改父组件传过来的props,如果子组件需要更新数据,子组件会将更新的数据传给父组件,由父组件进行更改,父组件更新完数据之后,重新传给子组件


五、Hooks使用规则

不应该在render方法中调用effect,effect在DOM渲染或更新完毕之后执行
useEffect在React的生命周期中相当于componentDidMount,componentDidUpdate,componentWillUnmount的结合
Hooks只能在顶层中使用,不允许在嵌套,条件和循环的函数中使用
只能通过React提供的方法调用Hooks


六、React16新特性

(1)render支持数组和字符串
(2)Fragment标签<></>
可以将一些子元素添加到DOM树上且不需要为这些元素提供额外的父节点,相当于render返回数组元素
(3)使用 Error Boundary处理错误组件
我们可以在容易出错的组件外层使用<ErrorBoundary></ErrorBoundary>标签包裹,当该节点有错误抛出的时候,整个组件结构就会根节点卸载,而不影响其他组件的渲染。
(4)Fiber
对React核心算法的一次重新实现,将原来的同步更新碎片化,避免主线程长时间阻塞,使渲染更加流畅。
(5)Strict Mode
Strict Mode可以在开发阶段开启严格模式,发现应用存在的潜在的问题,提升应用的健壮性
(6)Hooks
多个状态不会嵌套,依然平铺写法
Hooks可以引用其他Hooks
更容易的将组件的UI与状态进行分离
Hooks并不是通过Proxy或getters实现,而是通过数组实现,每次useState都会改变下标。


七、React三种声明组件的方式及区别

1.函数式定义的无状态组件
2.ES5原生方式React.createClass定义的组件
3.ES6形式的extends React.Component定义的组件

函数式定义的无状态组件

只有一个render方法的组件类,通过普通函数或者箭头函数的方式来创建。因为只有render方法,且是无状态组件,所以这种方式就不会有组件实例化的过程,因此就不需要分配多余的内存空间,一定程度上提高了性能。当然没有实例化过程自然就不能访问this对象。另外,由于是无状态组件,就不需要状态管理和生命周期函数了,所以,无状态组件是不能参与管理组件的生命周期无状态组件只能访问输入的props,不会有副作用

React.createClass方式创建组件

React.createClass方式是最早出现的创建组件的方式,是用ES5原生JavaScript的方式来创建组件。与无状态组件相比,该种方式和React.Component都是创建有状态的组件,这两种方式创建组件是要被实例化的,而且都可以访问组件的生命周期方法。但是,React.createClass方式创建组件有很大的缺陷。它会自动绑定函数方法(不像React.Component那样只关心需要绑定的函数),这样就导致了不必要的性能开销

React.Component方式创建组件

React.Component是以ES6的形式创建组件,是React目前最常用的创建有状态组件的方式,相对React.createClss方式创建组件,能更好的实现复用


八、React.createClass和React.Component的区别

(1)this绑定问题
React.createClass方式创建的组件,每一个成员函数的this都有React自动绑定,使用的时候直接this.method就可以,函数中的this会被正确的设置
React.Component方式创建的组件,不会自动绑定this,需要我们自己手动的去绑定,否则this就不能获得当前组件的实例对象
React.Component方式创建的组件,有3种方式绑定this
①.

constructor(props){
  super(props)
  this.method = this.method.bind(this) // 在构造函数中绑定
}

②.

<div onClick={this.method.bind(this)}></div> // 在标签上使用bind绑定

③.

<div onClick={() => this.method()}></div> // 使用箭头函数绑定

(2)组件属性类型(propTypes)和默认props(defaultProps)属性配置不同
React.createClass在创建组件时,有关组件的propTypes和组件的defaultProps会作为组件实例的属性来配置,其中defaultProps是使用getDefaultProps的方法获取默认组件属性

varTodoList = React.createClass({
  propTypes:{
    age: React.propTypes.string
  },
  getDefaultProps(){
    return{
      age: ''
	}
  }
  render(){
	return(
	  <div></div>
	)
  }
})

而React.Component在创建组件的时候,propTypes和defaultProps是作为组件类的属性,不是组件实例属性,也就是类的静态属性来配置的

class TodoList extends React.Component{
  static propTypes = {
	name: React.propTypes.string
  },
  static defaultProps = {
	name: ''
  }
}

(3)组件初始状态state的配置不同
React.createClass创建的组件,其状态state是通过getInitialState方法来配置组件相关的状态

const TodoList = React.createClass({
  getInitialState(){
	return{
	  disabled: false
	}
  }
  render(){
	return(
	  <div></div>
	)
  }
})

React.Component创建的组件,其状态state是在construct构造函数中初始化组件属性一样声明的

class TodoList extends React.Component{
  constructor(props){
	super(props)
	this.state = {
	  disabled: false
	}
  }
  render(){
	return(
	   <div></div>
	)
  }
}

九、React生命周期函数

(1)挂载卸载过程
1.1constructor()
constructor()中完成了React数据的初始化,它接受两个参数:props和context,当想在函数内使用这两个参数,需要使用super()方法传入这两个参数。只要使用了constructor()就一定要super(),否则会导致this指向错误

1.2componentWillMount()
该生命周期函数用的较少,这个生命周期函数代表的过程是组件已经经历constructor()初始化数据,但是还没有渲染DOM。它更多的是服务端渲染的时候使用

1.3componentDidMount()
组件第一次渲染完成,此时DOM节点已经生成,可以在这里调用Ajax请求,返回数据setState后组件会重新渲染

1.4componentWillUnmount()
在此处完成数据的销毁和组件的卸载
①.清除组件中所有的定时器setTimeout,setInterval
②.移除所有组件中的监听removeEventListener

(2)更新过程
2.1componentWillReceiveProps(nextProps)
在接受父组件改变后的props需要重新渲染组件的时候,这种情况下该生命周期函数用的比较多
①.接受一个参数nextProps
②.通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件

componentWillReceiveProps(nextProps){
  nextProps.age !== this.props.age && this.setState({
    age: nextProps.age
  }, () => {
	console.log(this.state.age: nextProps)
	//将state更新为nextProps,在setState的第二个参数(回调函数中可以拿到新的state)
  })
}

2.2shouldComponentUpdate(nextProps, nextState)
①.主要用于性能优化(部分更新)
②.唯一用于控制组件重新渲染的生命周期,由于在React中,当state发生变化,组件会进入重新渲染的过程,在该生命周期函数中return false可以阻止组件的更新
③.因为React父组件的重新渲染会导致所有子组件跟着一起重新渲染,当我们不需要子组件重新渲染的时候,我们可以在对应的子组件的该生命周期函数中进行阻止

2.3componentWillUpdate(nextProps, nextState)
shouldComponentUpdate返回true之后,组件进入重新渲染的过程,进入componentWillUpdate,这里也能拿到nextProps, nextState

2.4componentDidUpdate(prevProps, prevState)
组件更新完毕之后,React只会在第一次初始化成功的时候进入ComponentDidmount,而之后的每次重新渲染都会进入这个生命周期函数,这里可以拿到prevProps和 prevState,也就是更新之前的props和state

2.5render()
render方法会插入jsx生成DOM结构,会生成一棵虚拟DOM树,当每次组件更新的时候,React会通过diff算法来比较更新前后的新旧DOM树,比较之后,找到最小的有差异的DOM节点,重新渲染
(3)新增的生命周期
3.1getDerivedStateFromProps(nextProps, prevState)(谨慎使用)
代替componentWillReceiveProps()
该生命周期函数的作用是为了props能够更新到组件内部的props中。它的应用场景主要是:
①.无条件的根据props来更新组件内部的state,也就是只要传入props就更新state
②.只有props的值和state的值不相同时才更新state值
3.2getSnapshotBeforeUpdate(prevProps, prevState)
代替componentWillUpdate
componentWillUpdate是在组件更新前读取当前某个DOM元素的状态,然后在componentDidUpdate中进行相应的处理
而getSnapshotBeforeUpdate与componentDidUpdate的区别在于:
①.在React开启异步渲染模式之后,render阶段读取到的DOM元素状态并不总是和commit阶段相同,这样就导致了componentDidUpdate中使用componentWillUpdate中读取到的DOM元素状态是不安全的,可能这时候的值已经失效了。而getSnapshotBeforeUpdate会在render之前就调用。因此,getSnapshotBeforeUpdate中读取到的DOM元素状态是可以保证和componentDidUpdate中读取到的DOM元素状态一致的。该生命周期中返回的任何值都将作为参数传递给componentDidUpdate()


十、render方法的原理?返回的数据类型是什么

render函数是不做实际的渲染的,它只是返回一个jsx描述的结构,最终的渲染过程是交给React的,因此render函数也并不会DOM树上做装载或渲染内容。render函数应该是一个纯函数,完全根据props和state决定返回的结果,而不产生任何副作用。在render方法中调用this.setState是错误的,因为纯函数不应该引起状态的改变
render函数返回值:
1.元素
通常通过jsx创建

render(){
  return(<div></div>)
}

2.数组或DOM片段

render(){
  return [
	<div>1</div>
	<div>2</div>
  ]
}

3.字符串或数值类型:它们在DOM被渲染为文本节点

render(){
  return 123
}
render(){
  return 'abc'
}

4.布尔类型或null(什么都不渲染)

render(){
  return null
}

十一、在React中怎么阻止事件的默认行为

在HTML中,我们组织以默认行为可以使用return false。但是在React中阻止事件默认行为要用e.preventDefault()

function handleClick (e) {
  e.preventDefault()
}

以上就是我最近整理的关于React方面的面试题,后续应该还会有React-Router,Redux方面的面试题和对第一部分的补充。个人水平有限,有错误或者不当的地方请指正

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值