React面试题

650 阅读4分钟

你的技术栈主要是react,那你说说你用 react 有什么坑点

  1. JSX做表达式判断时候,需要强转为 boolean 类型,如:
render() {
	const b = 0;
  return <div>
    {
  		!!b && <div>这是一段文本</div>
  	}
	</div>
}

如果不使用 !!b 进行强转数据类型,会在页面里面输出 0

  1. 尽量不要在 componentWillReviceProps 里使用 setState,如果一定要使用,那么需要判断结束条件,不然会出现无限重渲染,导致页面奔溃。(实际不是 componentWillReviceProps 会无限重渲染,而是 componentDidUpdate)

  2. 给组件添加 ref 时候,尽量不要使用匿名函数,因为当组件更新的时候,匿名函数会被当做新的 prop 处理,让 ref 属性接收到新函数的时候,react 内部会先清空 ref,也就是会以 null 为回调参数先执行一次 ref 这个 props,然后在以该组件的实例执行一次 ref,所以用匿名函数做 ref 的时候,有时候去 ref 赋值后属性会取到 null

  3. 遍历子节点的时候,不要用 index 作为组件的 key 进行传入

怎么去设计一个组件封装

  • 组件封装的目的是为了重用,提高开发效率和代码质量
  • 低耦合,单一职责,可复用性,可维护性

react 的虚拟 dom 是怎么实现的

首先说说为什么要使用 Virturl DOM,因为操作真实 DOM 耗费的性能代价太高,所以 react 内部使用 js 实现一套 dom 结构,在每次操作和真实 dom 之前,使用实现好的 diff 算法,对虚拟 dom 进行比较,递归找出有变化的 dom 节点,然后对其进行更新操作。为了实现虚拟 DOM,我们需要把每一种节点类型抽象成对象,每一种节点类型有自己的属性,也就是 prop,每次进行 diff 的时候,react 会比较该节点类型,假如节点类型不一样,那么 react 会直接删除该节点,然后直接创建新的节点插入到其中,假如节点类型一样,那么会比较 prop 时候有更新,假如有 prop 不一样,那么 react 会判定该节点有更新,那么重渲染该节点,然后在对其子节点进行比较,一层一层往下,直到没有子节点。

react hooks 原理是什么

hooks 是用闭包实现的,因为纯函数不能记住状态,只能通过闭包来实现。

useState 中的状态是怎么存储的

通过单向链表,fiber tree 就是一个单向链表的树形结构。

如何遍历一个 dom 树

function traversal(node) {
  // 对 node 的处理
  if(node && node.nodeType === 1) {
  	console.log(node.tagName);
  }
  var i = 0,
      childNodes = node.childNodes,
      item;
  for(; i < childNodes.length; i++) {
  	item = childNodes[i];
    if(item.nodeType === 1) {
    	// 递归先序遍历子节点
      traversal(item);
    }
  }
}

数据双向绑定,单向绑定优缺点

双向绑定是自动管理状态的,对处理有用户交互的场景非常合适,代码量少,当项目越来越大的时候,调试也变得越来越复杂,难以跟踪问题

单向绑定是无状态的,程序调试相对容易,可以避免程序复杂度上升时产生的各种问题,当然写代码时就没有双向绑定那么爽了

React fiber 的理解和原理

  • 理解 React16 以前

对 virtural dom 的更新和渲染是同步的,就是当一次更新或者一次加载开始以后,diff virtural dom 并且渲染的过程是一口气完成的。如果组件层级比较深,相应的堆栈也会很深,长时间占用浏览器主线程,一些类似用户输入、鼠标滚动等操作得不到响应。借用Lin的两张图

1622794030(1).jpg

React16 Fiber Reconciler

React16 用了分片的方式解决上面的问题。就是把一个任务分成很多小片,当分配给这个小片的时间用尽的时候,就检查任务列表中有没有新的、优先级更高的任务,有就做这个新任务,没有就继续做原来的任务。这种方式被叫做异步渲染(Async Rendering)。

1622794074(1).jpg

  • 一些原理 Fiber 就是通过对象记录组件上需要做或者已经完成的更新,一个组件可以对应多个 Fiber。

在 render 函数中创建的 React Element 树在第一次渲染的时候会创建一颗结构一模一样的 Fiber 节点数。

不同的 React Element 类型对应不同的 Fiber 节点类型。一个 React Element 的工作就由它对应的 Fiber 节点来负责。

一个 React Element 可以对应不止一个 Fiber,因为 Fiber 在 update 的时候,会从原来的 Fiber(我们称为 current)clone 出一个新的 Fiber(我们称为alternate)。两个 Fiber diff 出的变化(side effect)记录在 alternate 上。所以一个组件在更新时最多会有两个 Fiber 与其对应,在更新结束后 alternate 会取代之前的 current 成为新的 current 节点。

其次,Fiber 的基本规则:

更新任务分成两个阶段,Roconciliation Phase 和 Commit Phase。Reconciliation Phase 的任务干的事情是,找出要做的更新工作(Diff Fiber Tree),就是一个计算阶段,计算结果可以被缓存。也就可以被打断;Commit Phase 需要提交所有更新并渲染,为了防止页面抖动,被设置为不能被打断。

PS:componentWillMount、componentWillReceiveProps、聪明ponentWillUpdate几个生命周期方法,在 Reconciliation Phase 被调用,有被打断的可能(时间用尽等情况),所以可能被多次调用。其实 shouldComponentUpdate 也可能被多次调用,只是它只返回 true 或者 false,没有副作用,可以暂时忽略。

解释 React 中 render()的目的

每个 React 组件强制要求必须有一个 render()。它返回一个 React 元素,是原生 DOM 组件的表示。如果需要渲染多个 HTML 元素,则必须将它们组合在一个封闭标记内 `` 此函数必须保持纯净,即必须每次调用时都返回相同的结果。

调用 setState 之后发生了什么

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

触发多次 setState,那么 render 会执行几次

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

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

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

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

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

为什么setState是一个异步的

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

原生事件和React事件的区别

  • React 事件使用驼峰命名,而不是全部小写
  • 通过 JSX,你传递一个函数作为事件处理程序,而不是一个字符串
  • React 中你不能通过返回 false 来阻止默认行为。必须明确调用 preventDefault

React的合成事件是什么

React 根据 W3C 规范定义了每个事件处理函数的参数,即合成事件。

事件处理程序将传递 SyntheticEvent 的实例,这是一个跨浏览器原生事件包装器。它具有与浏览器原生事件相同的接口,包括 stopPropagation()preventDefault(),在所有浏览器中他们工作方式都相同。

React 合成的 SyntheticEvent 采用了事件池,这样做可以大大节省内存,而不会频繁的创建和销毁事件对象。 另外,不管在什么浏览器环境下,浏览器会将该事件类型统一创建为合成事件,从而达到了浏览器兼容地目的。

什么是高阶组件(HOC)

高阶组件是重用组件逻辑的高级方法,是一种源于 React 的组件模式。HOC 是自定义组件,在它之内包含另一个组件。它们可以接受子组件提供的任何动态,但不会修改或复制其输入组件中的任何行为。你可以认为 HOC 是“纯(Pure)”组件。

你能用HOC做什么

HOC可以用于许多任务,例如:

  • 代码重用,逻辑和引导抽象
  • 渲染劫持
  • 状态抽象和控制
  • Props 控制