阅读 11451

[ React ] 面试题汇总

1.基础篇

1. React 中 keys 的作用是什么?

  • Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
render () {
  return (
    <ul>
      {this.state.todoItems.map(({item, key}) => {
        return <li key={key}>{item}</li>
      })}
    </ul>
  )
}
复制代码
  • react利用key来识别组件,它是一种身份标识标识,相同的key react认为是同一个组件,这样后续相同的key对应组件都不会被创建
  • 有了key属性后,就可以与组件建立了一种对应关系,react根据key来决定是销毁重新创建组件还是更新组件。
  • key相同,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新。
  • key值不同,则react先销毁该组件(有状态组件的componentWillUnmount会执行),然后重新创建该组件(有状态组件的constructor和componentWillUnmount都会执行)

2.调用 setState 之后发生了什么?

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

3.触发多次setstate,那么render会执行几次?

  • 多次setState会合并为一次render,因为setState并不会立即改变state的值,而是将其放到一个任务队列里,最终将多个setState合并,一次性更新页面。
  • 所以我们可以在代码里多次调用setState,每次只需要关注当前修改的字段即可。

[补充] react中如何对state中的数据进行修改?setState为什么是一个异步的?

  • 修改数据通过this.setState(参数1,参数2)
  • this.setState是一个异步函数
    • 参数1 : 是需要修改的数据是一个对象
    • 参数2 : 是一个回调函数,可以用来验证数据是否修改成功,同时可以获取到数据更新后的DOM结构等同于componentDidMount
  • this.setState中的第一个参数除了可以写成一个对象以外,还可以写成一个函数 ,函数中第一个值为prevState 第二个值为prePprops this.setState((prevState,prop)=>({}))

为什么建议传递给 setState的参数是一个callback而不是一个对象?

  • 因为this.props 和this.state的更新可能是异步的,不能依赖它们的值去计算下一个state

为什么setState是一个异步的?(请看3)

  • 当批量执行state的时候可以让DOM渲染的更快,也就是说多个setstate在执行的过程中还需要被合并

4.this.setState之后react做了哪些操作?

  • shouldComponentUpdate
  • componentWillUpdate
  • render
  • componentDidUpdate

5.简述一下virtual DOM (虚拟dom)如何工作?(4 & 5 取一回答)

  • 当数据发生变化,比如setState时,会引起组件重新渲染,整个UI都会以virtual dom的形式重新渲染
  • 然后收集差异也就是diff新的virtual dom和老的virtual dom的差异
  • 最后把差异队列里的差异,比如增加节点、删除节点、移动节点更新到真实的DOM上

5.为什么虚拟 dom 会提高性能?

  • 虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
  • 用 JavaScript 对象结构表示 DOM 树的结构
  • 然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。
  • 然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。

6.react diff 原理

  • 把树形结构按照层级分解,只比较同级元素。
  • 给列表结构的每个单元添加唯一的 key 属性,方便比较。
  • React 只会匹配相同class的component(这里面的class指的是组件的名字)
  • 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React检查所有标记 dirty 的 component 重新绘制.
  • 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。

7.React 中 refs 的作用是什么?(详细版本)

  • Refs 是 React 提供给我们的安全访问DOM元素或者某个组件实例的句柄。
  • 是父组件用来获取子组件的dom元素的
  • 我们可以为元素添加 ref 属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回
class CustomForm extends Component {
  handleSubmit = () => {
    console.log("Input Value: ", this.input.value)
  }
  render () {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type='text'
          ref={(input) => this.input = input} />
        <button type='submit'>Submit</button>
      </form>
    )
  }
}
复制代码
  • 上述代码中的 input 域包含了一个 ref 属性,该属性声明的回调函数会接收 input 对应的 DOM 元素,我们将其绑定到 this 指针以便在其他的类函数中使用。
  • 另外值得一提的是,refs 并不是类组件的专属,函数式组件同样能够利用闭包暂存其值
 function CustomForm ({handleSubmit}) {
  let inputElement
  return (
    <form onSubmit={() => handleSubmit(inputElement.value)}>
      <input
        type='text'
        ref={(input) => inputElement = input} />
      <button type='submit'>Submit</button>
    </form>
  )
}
复制代码

[详细易懂版本(推荐)]

1. ref设置为普通字符串

<button ref="myBtn"></button>
复制代码
  • 元素定义ref属性,后续可以通过 this.refs.myBtn 来获取这个真实DOM对象
  • 组件定义ref属性,后续可以通过 this.refs.myBtn 来获取这个组件的实例对象

2. ref设置为箭头函数

<button ref="{ (sl) => { this.myBtn = sl } }"></button>
复制代码
  • 元素定义ref属性,后续可以通过 this.myBtn 来获取这个真实DOM对象
  • 组件定义ref属性,后续可以通过 this.myBtn 来获取这个组件的实例对象

8.React 中有哪些构建组件的方式?

有什么区别?

  • 函数组件看似只是一个返回值是DOM结构的函数,其实它的背后是无状态组件的思想。
  • 函数组件中,你无法使用State,也无法使用组件的生命周期方法,这就决定了函数组件都是展示性组件,接收Props,渲染DOM,而不关注其他逻辑
  • 函数组件中没有this
  • 函数组件更容易理解。当你看到一个函数组件时,你就知道它的功能只是接收属性,渲染页面,它不执行与UI无关的逻辑处理,它只是一个纯函数。而不用在意它返回的DOM结构有多复杂。

9.事件处理函数的this指向问题

[补充] 事件处理函数如何传递参数?

  • 可以使用 bind 传递参数
  • 将事件处理函数交给箭头函数,然后在箭头函数里面调用我自己开始想要调用的那个方法,这时我已经是个普通函数了。

[注意!!!]

  • 如果一个事件处理函数传递了额外的参数,那么事件对象会摸摸的放置在最后一个里面

10.

2.十万个为什么篇

1.为什么列表循环渲染的key最好不要用index?

  • 举例说明

变化前数组的值是[1,2,3,4],key就是对应的下标:0,1,2,3 变化后数组的值是[4,3,2,1],key对应的下标也是:0,1,2,3

  • 那么diff算法在变化前的数组找到key =0的值是1,在变化后数组里找到的key=0的值是4
  • 因为子元素不一样就重新删除并更新
  • 但是如果加了唯一的key,如下:

变化前数组的值是[1,2,3,4],key就是对应的下标:id0,id1,id2,id3

变化后数组的值是[4,3,2,1],key对应的下标也是:id3,id2,id1,id0

  • 那么diff算法在变化前的数组找到key =id0的值是1,在变化后数组里找到的key=id0的值也是1
  • 因为子元素相同,就不删除并更新,只做移动操作,这就提升了性能

2.什么是状态提升?

3.什么是高阶组件?

4.什么是受控组件和非受控组件?

5.什么是纯函数?

6.什么是上下文Context?

7.react中的Portal是什么?

8.react16的错误边界(Error Boundaries)是什么?

3.生命周期(react17)篇

4.通信篇

1.父子通信

2.兄弟组件通信

3.跨多层次组件通信

4.任意组件间通信

5.差异篇

1.React与Vue的不同?

2.(组件的)状态(state)和属性(props)之间有何不同?

3.展示组件(Presentational component)和容器组件(Container component)之间有何不同?

4.在 React 当中 Element 和 Component 有何区别?

5.在 React 当中createElement 和 cloneElement 有什么区别?

6.路由(route)篇

7.redux篇

8.Mixin篇(I)

9.HOC篇(II)

10.React hooks篇(III)

文章分类
前端
文章标签