框架
react

初级

React的运行原理是什么?

分为调和阶段和提交阶段

  • 调和阶段是首先计算出状态变化后的虚拟DOM结构。然后计算出当前虚拟DOM结构修改为目标虚拟DOM结构的最优更新方案。
  • 提交阶段是将调和阶段记录的更新方案应用到DOM中,然后调用暴露给开发者的钩子方法。

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

  • 受控组件:只能通过React修改数据或状态的组件,就是受控组件。
  • 非受控组件:控件自己就能控制数据和状态的变更,而且React是不知道这些变更的,如 input, textarea, select, checkbox 等组件。

如何将非受控组件改为受控组件呢?那就是把组件数据或状态的变更,都交给React来操作。

setState 的传参方式,有什么区别?

setState()的传参有两种方式:纯数据和回调函数。回调函数方式能在参数中获得最新state,多用于异步的情况。

useEffect()的清除机制是什么?在什么时候执行?

useEffect(callback)的回调函数里,若有返回的函数,这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。

React 会在组件卸载的时候执行清除操作。若组件产生了更新,会先执行上一个的清除函数,然后再运行下一个 effect。

了解React的虚拟dom吗?

虚拟DOM是以对象的方式来描述真实dom的。在做更新操作的时候,可以在内存中进行数据比对, 减少对真实dom的操作,从而减少浏览器重排重绘的次数,从而能提高程序的性能。

什么是PureComponent?

React.PureComponent 与 React.Component 几乎完全相同,但 React.PureComponent通过prop和state的浅对比来实现更新。 跳过类式组件不必要的重新渲染。类式组件 可以通过继承 PureComponent 来选择此行为。 在函数组件中,通过memo来实现相同的效果。

react生命周期?

  • componentWillMount() 组件初始化时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state。
  • render() react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行。
  • componentDidMount() 组件渲染之后调用,只调用一次。
  • componentWillReceiveProps(nextProps) 组件初始化时不调用,组件接受新的props时调用。
  • shouldComponentUpdate(nextProps, nextState) 组件接受新的state或者props时调用,我们可以设置在此对比前后两个props和state是否相同, 如果相同则返回false阻止更新,这样就不需要创造新的dom树和旧的dom树进行diff算法对比。
  • componentWillUpdate(nextProps, nextState) 组件初始化时不调用,只有在组件将要更新时才调用,此时可以修改state。
  • componentDidUpdate() 组件初始化时不调用,组件更新完成后调用,此时可以获取dom节点。
  • componentWillUnmount() 组件将要卸载时调用,一些事件监听和定时器需要在此时清除。

什么是合成事件(事件代理),与原生事件有什么区别?

React中所有触发的事件,都在React实现的一个合成事件层。作用是

  • 规范化了这些接口在不同浏览器中的行为,抹平了不同浏览器事件对象之间的差异,解决了浏览器兼容的问题,方便事件统一管理;
  • 避免垃圾回收;react事件对象不会被释放掉,而是存放进一个数组中,避免频繁地去创建和销毁(垃圾回收)。

对合成事件阻止不会影响原生事件的执行。

在React底层,主要对合成事件做了两件事:

  • 事件委派: React会把所有的事件绑定到结构的最外层(Virtual DOM根节点),使用统一的事件监听器, 这个事件监听器上维持了一个映射来保存所有组件内部事件监听和处理函数。 react 事件不能采用 return false 的方式来阻止浏览器的默认行为,而必须要地明确地调用preventDefault()来阻止默认行为。
  • 自动绑定: React组件中,每个方法的上下文都会指向该组件的实例,即自动绑定this为当前组件。

key 的作用是什么?

当出现大量相同的标签时,使用key可以帮助diff算法提升判断的速度,从而使页面渲染更快消耗更少。

多次执行 setState(),会触发多次更新吗?

在 React18 中,无论是多个 useState()的 hook,还是操作(dispatch)多次的数据。只要他们在同一优先级,React 就会将他们合并到一起操作,最后再更新数据。 这是基于 React18 的批处理机制。React 将多个状态更新分组到一个重新渲染中以获得更好的性能。(将多次 setstate 事件合并); 在 v18 之前只在事件处理函数中实现了批处理,在 v18 中所有更新都将自动批处理,包括 promise 链、setTimeout 等异步代码以及原生事件处理函数;

中级

Redux 中异步的请求怎么处理

用中间件,但是只不过就是从请求回调里调用dispatch变成dispatch的参数设置成请求函数,换个写法可能更易读,但代码量一点也没少

React.forwardRef是什么?它有什么作用?

React.forwardRef 会创建一个React组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见,但在以下两种场景中特别有用:

  • 父组件调用子组件的方法
  • 把子组件的refs转发上去

如何实现组件的懒加载?

从 16.6.0 开始,React 提供了 lazy 和 Suspense 来实现懒加载。

import React, { lazy, Suspense } from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));
 
function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <OtherComponent />
    </Suspense>
  );
}

属性fallback表示在加载组件前,渲染的内容。

高阶组件(HOC)?

高阶组件通过包裹(wrapped)被传入的 React 组件,经过一系列处理,最终返回一个相对增强(enhanced)的 React 组件,供其他组件调用。

React 的 diff 过程?

diff算法会高效地对比新旧虚拟DOM,找出真正的Dom变化之处,react中diff算法主要遵循三个层级的策略:

  • tree层级,只会对相同层级的节点进行比较,而且只有创建、删除操作,没有移动操作
  • conponent 层级,如果是同一类的组件,则会继续往下diff运算,如果不是同一类的组件,那么直接删除这个组件下的所有子节点,创建新的
  • element 层级,每个节点在对应的层级用唯一的key作为标识,提供了插入,移动,删除操作

通过key可以准确地发现新旧集合中的节点都是相同的节点,因此无需进行节点删除和创建,只需要将旧集合中节点的位置进行移动,更新为新集合中节点的位置

React和Vue的diff算法的异同?

共同点:

  • vue和diff算法,都是只做同级比较,不进行跨层级比较 不同点:
  • vue对比节点时,当节点元素类型相同,类名不同时,认为是不同的元素,删除重新创建,而react认为是同类型的节点,进行修改操作。
  • vue的列表比对,采用从两端到中间的比对方式,而react则采用从左到右依次比对的方式。 例如当一个集合,只是把最后一个节点移动到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移动到第一个。

redux原理?

将所有状态进行集中管理

高级

什么是React Portal?有哪些使用场景?

ReactDOM.createPortal(child, container)

Portals 提供了一种脱离 #app 的组件。特别是 position: absolute 与 position: fixed 的组件。比如模态框,通知,警告,goTop 等。

cloneElement 与 createElement 各是什么,有什么区别?

  • cloneElement,根据 Element 生成新的 Element,新添加的属性会并入原有的属性 一般配合 React.children.map使用,如用于动态地给子组件添加更多 props 信息、样式
  • createElement,根据 Type 生成新的 Element, 第一个参数是 type 简单来说就是各种标签名字,第二个参数是传入的属性,第三个参数以及之后的参数就是作为组件的子组件。

React16的fiber架构?

默认情况下,JavaScript 运算、页面布局和页面绘制都是运行在浏览器的主线程当中,他们之间是互斥的关系, 如果 JavaScript 运算持续占用主线程,页面就没法得到及时的更新, 阻塞了浏览器的 UI 渲染,Fiber 架构将更新任务分解成小的、可中断的单元,分批完成, 也就是说在完成一部分任务之后,将控制权交回给浏览器,让浏览器有时间进行页面的渲染,及时地响应用户的交互。

React18 有哪些新变化?

react18以于2022年3月29日发布正式版本。

  • Concurrent Mode(以下简称 CM)翻译叫并发渲染机制:在以前,React 在状态变更后,会开始准备虚拟 DOM,然后渲染真实 DOM,整个流程是串行的。一旦开始触发更新,只能等流程完全结束,期间是无法中断的。 在 CM 模式下,React 在执行过程中,每执行一个 Fiber,都会看看有没有更高优先级的更新,如果有,则当前低优先级的的更新会被暂停,待高优先级任务执行完之后,再继续执行或重新执行。
  • startTransition:主动降低优先级。本质上是用于一些不是很急迫的更新上,用来进行并发控制,在v18之前,所有的更新任务都被视为急迫的任务,而CM模式能将渲染中断,可以让高优先级的任务先更新渲染。 比如「搜索引擎的关键词联想」,用户在输入框中的输入希望是实时的,而联想词汇则不是很在意。我们可以用 startTransition 来降低联想词汇更新的优先级。
  • 入口优化:现在是要先通过createRoot()创建一个 root 节点,然后该 root 节点来调用render()方法;后面再想重新渲染整个应用的时候,通过root节点进行render,就不用重新渲染这个根结点了。
  • 全部自动批处理优化:批处理是 React 将多个状态更新分组到一个重新渲染中以获得更好的性能。(例如将多次 setstate 事件合并); 在v17的批处理只会在事件处理函数中实现,在 v18 中所有更新都将自动批处理,包括 promise 链、setTimeout 等异步代码。
  • useId:主要用于 SSR 服务端渲染的场景,方便在服务端渲染和客户端渲染时,产生唯一的 id;

React的并发问题是如何处理的?

React 中的并发,是JS的逻辑代码会跟 UI 渲染竞争主线程。若一个很耗时的任务占据了线程,那么后续的执行内容都会被阻塞。 为了避免这种情况,React 就利用 fiber 结构和时间切片的机制,将一个大任务分解成多个小任务,然后按照任务的优先级和线程的占用情况,对任务进行调度。

  • 对于每个更新,为其分配一个优先级 lane,用于区分其紧急程度。
  • 通过 Fiber 结构将不紧急的更新拆分成多段更新,并通过宏任务的方式将其合理分配到浏览器的帧当中。这样就能使得紧急任务能够插入进来。
  • 高优先级的更新会打断低优先级的更新,等高优先级更新完成后,再开始低优先级更新。

React.Children.map 和 js 的 map 有什么区别?

JavaScript 中的 map 不会对为 null 或者 undefined 的数据进行处理,而 React.Children.map 中的 map 可以处理 React.Children 为 null 或者 undefined 的情况。

redux与vuex的区别?

  • redux使用的是不可变数据,每次都是用新的state替换旧的state,通过diff算法比较差异的;而Vuex是可变的,通过getter/setter直接修改。
  • 另外就是在api上有不同,vuex定义了state,getter,mutation,action;redux定义了state,reducer,action。

说一下Redux功能化编程的概念?

在redux中使用了功能化编程的概念。在参数中可以传递函数。使用了数据流控制, 递归调用, 函数和数组等等。帮助函数, 如reduce和map filter被大量使用。 允许函数的串联。状态只读。代码执行顺序的优先级没有必要考虑。

useState的默认值是props会发生什么?

useState只执行一次,state不会随着props变化