平平无奇古哥哥

V1

2023/05/23阅读:28主题:橙心

前端面试:React高频面试题「2023」

本文收录于我写的《前端面试手册》,想获得更好的阅读体验请访问在线地址:https://gugiegie.gitee.io/frontend

1. setState是同步还是异步?

  1. React是希望setState表现为异步的,因为批量更新可以优化性能。因此在React能够管控到的地方,比如生命周期钩子和合成事件回调函数内,表现为异步。
  2. 在定时器和原生事件里,因为React管控不到,所以表现为同步。
  3. 在某些情况下,我们需要立即获取更新后的状态,这时可以使用第二个可选参数callback,在状态更新后立即执行回调函数来获取更新后的状态。例如:
this.setState({ counterthis.state.counter + 1 }, () => {
  console.log(this.state.counter); // 输出更新后的值
});

2. React组件通信方式有哪些?

老生常谈的题了,背答案不是目的,好的回答是对于每种场景能举出自己在实际项目中的应用。

  1. Props: 父组件可以通过 props 将数据传递给子组件。子组件可以通过 this.props 访问这些数据。
  2. Callback: 父组件可以通过回调函数将函数传递给子组件。子组件可以在适当的时候调用这些回调函数,以便与父组件通信。
  3. Context: 上下文是一种在组件树中共享数据的方法。通过 context,可以在组件树中传递数据,而不需要在每个级别显式地将 props 传递给所有组件。
  4. Redux: 复杂应用全局状态管理可以使用Redux、Mobx等状态管理库,项目里一般使用React Redux或者RTK工具包。
  5. Pub/Sub: 发布/订阅模式是一种通过事件来进行任意组件间通信的方法,和Vue里的事件总线原理一样。
  6. Hooks里也可以通过useReducer和useContext来实现全局组件通信。

3. 在项目中怎么使用Redux?

Redux是一个通用的JS库,一般在React项目里不会直接使用Redux,在项目中使用的一般是react-redux。

现在React官方推荐的是RTK工具包,使用起来更简洁,不用写很多样板代码,代码可读性更好。

当然也可以根据公司的需求,自研状态管理方案。

4. Redux中间件是什么?实现原理?

中间件的本质就是个函数,在Redux每次写数据的时候执行,用来实现一些通用的功能。

常见的中间件功能包括异步中间件、持久化中间件、log中间件。

Redux中间件的实现原理和Koa中间件、Axios拦截器类似,数组里面存函数,然后compose调用中间件函数,并传递参数给中间件。

5. 函数式组件和类组件有什么区别?

可以从写法,逻辑复用等角度来谈一谈区别。

  1. 类组件:有props、state和生命周期,可以实现复杂UI和交互。
  2. 函数式组件:在Hooks之前,函数式组件就是纯渲染组件,接收props返回React元素。Hooks之后,函数式组件有了能表达交互的能力。
  3. 逻辑复用:类组件可以通过HOC和Mixin实现逻辑复用,函数式组件可以通过Hooks实现逻辑复用。

6. React逻辑复用方式有哪些?

组件封装和逻辑复用,是前端进阶必备的,小伙伴们可以多花点时间深入研究,这里只是简要总结。

  1. Mixin:有很多缺点,已被弃用,可以不考虑。
  2. HOC(高阶组件):高阶组件是一个函数,它接收一个组件作为参数并返回一个新的组件。高阶组件可以将一些通用的逻辑(如:数据获取、权限验证、错误处理等)封装到一个函数中,并将其作为高阶组件的参数传递给其他组件使用,HOC一般以withXxx命名,并可以结合装饰器优雅地使用。
  3. Render Props:通过在组件中传递一个函数作为prop,该函数将用于渲染组件的内容。这个函数可以接收组件需要的数据和方法,并返回React元素。
  4. Hooks:自定义Hooks,将通用逻辑封装到useXxx函数中,可以在多个组件内使用,常见的像数据请求、表单、防抖节流、拖拽等。

7. 使用Hooks有踩过哪些坑?

  1. useEffect中没有正确设置依赖数组导致死循环。
  2. useEffect中没有清除副作用导致内存泄漏。
  3. 在条件语句和循环中使用Hooks导致报错。
  4. 闭包陷阱。

8. useRef有什么作用?

  1. 获取DOM。
  2. 存储上一次渲染的值,可以用useRef创建一个对象来存储setState前的旧值。

7. useMemo和useCallback有什么作用?

  1. useMemo类似于Vue的计算属性。
import React, { useMemo } from 'react';

function ExpensiveComponent({ data }{
  const expensiveResult = useMemo(() => {
    // 计算昂贵的结果
    return data.filter(item => item > 10);
  }, [data]);

  return <div>{expensiveResult}</div>;
}
  1. useCallback:大多数人认为useCallback的作用是缓存函数的生成,但在实际应用中这种优化是微不足道的,useCallback真正的作用是在函数需要作为prop传递给子组件时,使用useCallback包裹可以避免子组件无谓的更新。

9. React代码层面有哪些性能优化的方式?

  1. React.memo():可以缓存组件的渲染结果,避免不必要的重渲染。它接受一个函数组件,并返回一个新的组件,新组件将只在props发生变化时才重新渲染。
  2. useMemo和useCallback。
  3. shouldComponentUpdate:在类组件中,可以通过实现shouldComponentUpdate()方法来判断组件是否需要重新渲染。SCU接收两个参数:nextProps和nextState,我们可以在这个方法中比较当前props和state与下一个props和state的变化来决定是否需要重新渲染组件。
  4. 使用React.lazy()和Suspense进行组件懒加载。
  5. 对于大型应用,可以使用不可变数据的三方库比如Immer.js结合shouldComponentUpdate来做性能优化。

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

受控组件和非受控组件是针对表单的。

1. 受控组件:(类似于Vue的双向绑定)

  1. React默认不是双向绑定的,也就是说当我们在输入框输入的时候,输入框绑定的值并不会自动变化。

  2. 通过给input绑定onChange事件,让React实现类似于Vue的双向绑定,这就叫受控组件。

2. 非受控组件

  1. 非受控组件是让用户手动操作Dom来控制表单值。

  2. 非受控组件的好处是更自由,可以更方便地自行选择三方库来处理表单。

11. 什么是eject?

npm run eject命令可以将 create-react-app 创建的 React 项目里的配置文件暴露出来,以便我们自定义配置。

比如以下场景:

  1. 配置Less等预编译器语法。
  2. 使用PostCSS工具来做移动端自动适配。
  3. 搭建Typescript开发环境。
  4. Webpack优化。

12. Hooks实现原理?

篇幅有限,这里只做简要总结,实现细节后续会出文讲解。

1. memorizedState:Fiber节点上有个属性叫memorizedState,所有的Hooks都是围绕这个memorizedState来实现的,把要存的状态和函数队列存到这个属性上,然后按需求做增删改查就行了。

2. 链表存储状态:为了保证Hooks状态的序列,React采用链表来保存函数式组件的state,使用next属性来连接前后两个状态序列。

13. JSX和模板引擎有什么区别?

  1. JSX:更加灵活,既可以写标签,也可以使用JS语法和表达式,在做复杂渲染时更得心应手。

  2. 模板引擎:更简单易上手,开发效率高,结合指令的可读性也比较好。

  3. JSX太灵活就导致没法给编译器提供太多的优化线索,不好做静态优化,模板引擎可以在编译时做静态标记,性能更好。

  4. JSX只是个编译工具,Vue经过一定的配置也可以使用。

14. 怎么理解Fiber和并发模式?

1. 为什么要设计并发模式?

在React的旧版本中,当组件状态发生变化时,React会将整个组件树进行递归遍历,生成新的虚拟DOM树,并与旧的虚拟DOM树进行比较,找出需要更新的部分,然后将这些部分更新到DOM中。这种遍历方式虽然简单,但是在组件树变得非常大、复杂的情况下,会导致渲染和更新性能下降,造成页面卡顿甚至无法响应用户操作的情况。为了解决这个问题,React引入了并发模式。

2. Fiber是什么?

  1. Fiber是一种数据结构,由VDOM转化生成。
  2. Fiber的思想是将组件树的遍历过程拆分成多个小的、可中断的任务,以实现更细粒度的控制和优化。
  3. 具体来说,Fiber将每个组件看作是一个执行单元,并将组件树转换成一棵Fiber树。每个Fiber节点都包含了组件的状态和一些额外的信息,例如优先级、副作用等。
  4. 在更新过程中,React会根据Fiber节点的优先级,将Fiber树转换成一个任务队列,然后按照优先级进行调度和执行。React还会利用浏览器提供的requestIdleCallback API来分配空闲时间,以避免阻塞渲染线程。
  5. 由于Fiber将组件树的遍历过程拆分成了多个小的、可中断的任务,因此React可以在需要更新的部分进行优化,从而提高渲染和更新的性能。例如,在执行更新任务时,React可以根据优先级调整任务的执行顺序,避免低优先级任务阻塞高优先级任务的执行,提高了应用程序的响应速度和性能。

15.Vue和React有哪些区别,你更喜欢哪个?

这是个开放题,不建议背答案,可以从生态、语法、性能、原理、开发体验等方面去试着比较一下。

网上关于这两个框架谁更好争吵的也很凶,实际上无论是Vue和React,都可以开发出优秀的应用,关键在于如何正确使用。与其争论哪个更好,不如两个都学好,拿着高薪看别人对线它不香嘛。

16. 有了解过哪些类React框架,谈谈你对它们的看法?

Preact:可以理解为简易版React,但是和React有一样的API,性能比React还好,甚至也实现了并发模式,对于想要阅读React源码又觉得难的同学,可以看一看Preact的源码。

Svelte:无虚拟Dom,依靠编译器和纯响应式的轻量级框架,然而性能却非常好。

SolidJS:和Svelte类似,但是SolidJS的语法更接近于React,Svelte的语法接近Vue。

总结一下:

  1. 这些类React或者类Vue框架,可能在某一方面或者某些方面表现很出色。
  2. 在开发成熟项目时,还是尽量选择Vue和React,因为毕竟生态和解决方案更多。
  3. 时间允许的话推荐阅读这些框架的源码,它们的代码量相对少,容易阅读,能让你有一个更广的视野来看待前端框架的原理和设计思路。

分类:

前端

标签:

前端

作者介绍

平平无奇古哥哥
V1