Skip to content

react 面试题整理

Published: at 01:05 PM
  1. 讲一下React的Fiber架构?

    React Fiber 是 React 16 中新增的一种调度算法,主要用于实现对组件的增量渲染(递增渲染)。它采用了浏览器的 requestIdleCallback API,在主线程有空闲时间时安排任务,可以让 React 在长时间运行任务时,中断执行去响应用户的交互,然后在稍后继续下一步渲染工作。这样有效提高了应用程序的响应速度。

    • 增量渲染:实现了任务拆分,将任务拆分为一小块一小块的单元,每次只执行一小部分任务后释放控制权交还给主线程,避免长时间占用主线程。

    • 优先级调度:React Fiber 引入了任务优先级的概念,不同类型的更新有不同的优先级,通过这种方式,React 可以在主线程空闲时处理优先级较低的任务,优先级较高的任务则可以立即执行,从而优化用户体验

    • 并发模式:React Fiber 开启了 React 的并发模式(Concurrent Mode)的可能,使得 React 可以在同一时间处理多个状态的更新,根据优先级决定先处理哪个。 错误边界处理:

    Fiber Reconciler(也就是 Fiber)采用了“时间分片”策略,使得渲染过程可以被打断,变得可控,并且可以优先执行紧急任务。例如,当用户点击按钮时,React 会暂停正在进行的渲染工作,先执行这个更为紧急的任务,然后再去完成原来的工作。

    以下是 Fiber 原理的主要部分:

    • Fiber 节点:每个 React 组件都有一个或多个 fiber 节点与之对应。它是一个普通的 JavaScript 对象,保存了组件的类型(函数组件、类组件等)、当前状态和其它信息。

    • 双缓存:Fiber 为每个节点创建了一个副本,一个用于显示(current Fiber),另一个用于在内存中进行计算(workInProgress Fiber)。它们分别代表了节点的两个状态:当前屏幕上的状态和下一帧的新状态。

    • 链表遍历:Fiber 构建了一种链表的数据结构来描述组件树。这样在遍历更新组件树时,可以灵活地暂停、继续和重启,并且可以优先处理优先级较高的更新。

    • 分阶段计算:Fiber 将组件更新任务拆分为两个阶段:Render 阶段和 Commit 阶段。在 Render 阶段,会构建 workInProgress Fiber 树,并找出需要更新的节点,在这个阶段可以打断和重启;Commit 阶段主要将 workInProgress Fiber 树渲染到页面上,这个阶段不能中断。

  2. 说一下 React diff 算法?

    两个不同类型的元素将产生不同的树---卸载旧树,构建新树

    根据不同的key和props来指示哪些子元素可以在不同的渲染下保持稳定---处理列表项,更精准的移动插入或删除。

    运作过程:

    a. Tree Diff(主要是为了解决 “节点跨层级移动”): 这个阶段是对树的各个节点进行比较,类型相同则对比更新不同的内容和属性(递归比较子节点);不同类型则卸载旧树,构建新树。 对整个树进行深度优先遍历;

    b. Component Diff: 这个阶段是对组件进行比较,组件类型相同则按照原始规则进行setState,然后比较虚拟DOM;若不是则删除该组件及其子组件,重新生成新的组件。

    c. Element Diff: 这个阶段主要处理子节点DOM节点的比较,对子节点进行简单的增删改操作,通过唯一的key识别各节点之间的关系,减少节点交替,提高性能。

    对比同层级下的子节点集合进行差异对比;通过 key 值进行节点匹配,找出需要被更新、插入和删除的节点。

  3. 说一下对 React Hook 的理解,它的实现原理,和生命周期有哪些区别?

    hooks的实现原理:

    React Hook 的实现原理主要通过链表和数组的数据结构来实现的:

    a. 在 React 中,每个组件对应一个 fiber 实例,fiber 实例的 memoizedState 属性是一个链表,这个链表用于存储这个组件中所有 Hooks 的状态;

    b. 当你在代码中调用 useState 或 useEffect 等 Hook 时,实际上是向这个链表中添加一个节点,这个节点存储了对应 Hook 的状态和其他信息。调用顺序决定了它在链表中的位置

    c. 当组件重新渲染时,React 将按照旧的顺序来消费这个链表中的状态,也就是说,先调用的 useState 对应的是链表的前面部分,后面调用的 useState 或 useEffect 对应的则是链表后面的部分。

    d. 由于每次渲染都会按照相同的顺序调用 Hook,因此所有的 state 和 effect 总能找到它在链表中的对应位置,进而保证了状态的正确性

    e. 对于 useEffect,React 在执行一个 effect 时,并不会立即执行它,而是将它放到一个单独的数据结构中,在所有的组件都渲染完毕之后再统一执行。这使得 React 有能力合理地安排和优化 effect 的执行顺序,以保证性能。

  4. React Hook 实现原理中,为什么要依赖 Hooks 的调用顺序?

    React 内部会为每一个函数组件维护一份“state 列表”,这个列表中保存了函数组件中每一个 Hook 的状态。保持 Hook 调用顺序的一致,也就意味着 React 能够准确地知道当前操作或获取的 state 对应于哪个 Hook,从而能够保证 state 的正确性。

    如果在条件、循环或者嵌套函数中调用 Hook,可能会打破这个固定的调用顺序,React 就失去了确定对应关系的依据,无法正确地获取和更新对应的 state,可能导致一些难以预期的问题。

  5. react受控组件和非受控组件

    受控组件:受控组件是指输入数据受到 React 组件控制的表单元素。在受控组件中,表单数据由 React 组件的 state 管理,并通过 setState 方法进行更新。这意味着我们可以对输入的数据进行控制,如进行格式化、验证等操作

    非受控组件:非受控组件是指输入数据由 DOM 自身处理的表单元素。在非受控组件中,我们并不直接操作表单的数据,当我们需要表单的数据时,我们就从 DOM 中取出。这通常通过 ref 实现。

  6. 说一下 Redux 的原理,介绍下整体的一个工作流程

    Store(状态容器):在 Redux 中,所有的状态都保存在一个单一的对象之中,这个对象就是 store。

    Action(行为):Action 是用于描述发生了什么的简单对象。每个 Action 对象必须包含一个 type 属性,用于指定操作的类别。

    Reducer(处理函数):Reducer 是根据指定的 Action 更新状态的函数。Reducer函数接收当前的状态和一个 Action,然后返回一个新的状态。

    派发 Action -> Reducer 处理 Action -> 生成新的 State -> 触发 Listener。其目标是使得状态更新的过程更加可预测和可追踪

  7. React 组件中怎么做事件代理?它的原理是什么?

    a. 统一事件监听:为了避免在每个 DOM 节点上都添加事件监听,React 会在 document 根节点上统一添加事件监听,所有的事件都会被统一收集处理。

    b. SyntheticEvent 对象:React 中构建了一套 SyntheticEvent 合成事件系统,原生事件发生后,会被 React 接收,然后封装为完全符合 W3C 规范的 SyntheticEvent 对象,并在更新组件时按照事件流(捕获、目标、冒泡阶段)的顺序批量运行。

    事件代理的优点包括减少内存消耗,避免频繁的操作真实 DOM 而引发重排重绘,实现动态监听新添加的元素等。

  8. 柯里化函数?

    柯里化是一种将接受多个参数的函数转换为接受单一参数的函数,然后返回接受余下的参数且返回结果的新函数的技术。也可以理解为,柯里化是一种通过减少参数的数量来创建新函数的方式。

    好处:参数复用/延迟执行/提前返回/支持管道—通过柯里化,可以创建一个管道。管道就是一系列的函数调用,每个函数的输出是下一个函数的输入。

  9. react 异步渲染的概念,介绍 Time Slicing 和 Suspense?

    Time Slicing 是指,React 将渲染工作(也就是 virtual dom 的计算工作)分割成小块(chunk),中断和恢复渲染工作。这使得 React 可以将 CPU 和浏览器主线程的掌控权交还给浏览器,使得浏览器可以保持流畅,然后在浏览器空闲的时候,再恢复这些渲染工作。

    在新的调度模型下,React 在开始更新组件树时会先估算每个更新的优先级,高优先级的任务(如:用户交互、首次渲染等)会先执行,而低优先级的任务(如setState等)可以被打断,暂停开始执行,然后在浏览器空闲时再继续执行,最大限度地利用了浏览器的空闲时间,从而提高了性能。

    Suspense 是 React 用于管理组件异步加载的一种机制。React 16.6 版本引入了 Error Boundaries,这是一个可以捕获其子组件树 JavaScript 异常、记录这些信息并展示备用 UI 的 React 组件。

    React Suspense 就是基于同样的理念,但它处理的不是 JavaScript 异常,而是组件的异步加载。

    Suspense 可以包裹在任何需要异步加载的组件树最外层,并提供一个 loading fallback 的 UI,当组件树中的任何一个组件未完成加载时就渲染出这个 UI,只有当所有组件都完成加载后,才会渲染真实的 UI。

    有了 Suspense,我们就可以非常轻松地为组件添加异步加载的功能,而无需关心复杂的状态管理,大大提高了开发效率。

  10. 如何解决 props 层级过深的问题?

    使用 Context API/redux或者mobx/组件复用

  11. 渲染十万条数据解决方案?

    分页/虚拟列表/懒加载/滚动加载/Web Worker