React面试宝典[updating]

React面试宝典

一、组件基础

梳理组件设计原理与思路分析和解决问题的技巧

1.React原理

📌解释React是什么(谈一谈React理解)

概念:一句话解释技术本质

  • react是一个网页UI框架

    回顾历史 ①jQuery诞生于2005年,浏览器兼容性是当时最大的问题,jQuery应运而生,封装了Ajax,样式选择器,封装链式选择器;jQuery并没有解决代码如何组织的问题,甚至不能称之为框架,本质上它只是一个工具函数合集,没有模式的方式将前端三件套组合在一起
    ②Angularjs概念多,庞大复杂,双向绑定的特色,值与视图的更新
    主要问题 ①与后端不同的是前端主要以UI组件为基础需要一个可以使组件复用的开发方
    ②前端工程越来越庞杂,组件作为基本单位,应该是可以通过编写单元测试来维持稳定性的
    react React中只有组件,没有页面,没有控制器,也没有模型
    只关心数据和组件
    构建UI试图时,组合组件始终是最优的解决方案
    核心思路 1声明式:直观,开发人员只需要描述他们想要实现什么目的,无须列出实现效果的所有步骤
    2组件化:降低系统功能的耦合性
    3通用性:react将dom抽象为虚拟dom,开发者并不会直接操作dom使得react不会受限于web开发

用途:简短说明技术用途

  • 构建视图

思路:概要说明核心技术思路、运作流程

优缺点:独特优势、个别缺点

  • 声明式、组件化、通用性

  • 学习成本高


答题模板

1.React是一个网页UI框架,通过组件化的方式解决视图层开发复用的问题,本质是一个组件化框架。
2.它的核心设计思路有三点,分别是声明式、组件化与通用性。
①声明式的优势在于直观与组合。
②组件化的优势在于视图的拆分与模块复用,可以更容易做到高内聚低耦合。
③通用性在于一次学习,随处编写。比如React Native,React 360等,这里主要靠虚拟DOM来保证实现。
3.这使得 React的适用范围变得足够广,无论是Web、Native、VR,甚至Shell应用都可以进行开发。这也是React的优势。
4.但作为一个视图层的框架,React的劣势也十分明显。它并没有提供完整的一揽子解决方案,在开发大型前端应用时,需要向社区寻找并整合解决方案。虽然一定程度上促进了社区的繁荣,但也为开发者在技术选型和学习适用上造成了一定的成本。
5.可以谈谈对React优化、对虚拟dom的看法
6.谈谈react相关的工程架构与设计模式

2.JXS

📌为什么React要使用jsx(为什么不用别的)

一句话解释jsx:jsx是一个JavaScript的语法拓展,类似xml的ecmascript语法扩展

核心概念:用来解决什么问题

  • react本身并不强制使用jsx,可以使用react.createElement函数

  • 使用jsx(语法糖),实际上在运行的时候,会使用 Babel 插件将 JSX 语法的代码还原为 React.createElement 的代码。

  • react需要将组件转化为虚拟dom树,所以我们在编写代码时,实际上是在手写一棵结构树。xml在树的结构的描述上天生具有可读性强的优势

方案对比

关注点分离:将代码分隔为不同部分的设计原则

当关注点分开时,各部分可以重复使用,以及独立开发和更新

具有特殊价值的是能够稍后改进或修改一段代码,而无须知道其他部分的细节必须对这些部分进行相应的更改

  • 模板 引入概念多、弱关注度分离

  • 模板字符串 结构描述复杂、语法提示差

  • jxon 语法提示差

  • jsx优点:

    • 快速,JSX执行更快,因为它在编译为JavaScript代码后进行了优化

    • 安全,与JavaScript相比,JSX是静态类型的,大多是类型安全的。使用JSX进行开发时,应用程序的质量会变得更高,因为在编译过程中会发现许多错误,它也提供编译器级别的调试功能。

答题模板

JSX 是一个 JavaScript 的语法扩展,结构类似 XML。

JSX 主要用于声明 React 元素,但 React 中并不强制使用 JSX。即使使用了 JSX,也会在构建过程中,通过 Babel 插件编译为 React.createElement。所以 JSX 更像是 React.createElement 的一种语法糖。

所以从这里可以看出,React 团队并不想引入 JavaScript 本身以外的开发体系。而是希望通过合理的关注点分离保持组件开发的纯粹性。

接下来与 JSX 以外的三种技术方案进行对比。

首先是模板,React 团队认为模板不应该是开发过程中的关注点,因为引入了模板语法、模板指令等概念,是一种不佳的实现方案。

其次是模板字符串,模板字符串编写的结构会造成多次内部嵌套,使整个结构变得复杂,并且优化代码提示也会变得困难重重。

最后是 JXON,同样因为代码提示困难的原因而被放弃。

所以 React 最后选用了 JSX,因为 JSX 与其设计思想贴合,不需要引入过多新的概念,对编辑器的代码提示也极为友好。

📌Babel插件如何实现jsx到js的编译

Babel 读取代码并解析,生成 AST,再将 AST 传入插件层进行转换,在转换时就可以将 JSX 的结构转换为 React.createElement 的函数。

JSX → 使用 React 构造组件,bable 进行编译 → JavaScript 对象 → ReactDOM.render() →DOM元素→插入页面

3.生命周期

4.组件类型

📌类组件和函数组件的区别

共同点:实际用途一样,函数组件和类组件使用方式和最终呈现效果上是完全一致的

不同点:本质上代表两种不同设计思想与心智模式

1. 基础认知:

  • 类组件的根基是OOP,面向对象编程

    • 通过使用ES6类的编写形式去编写组件,该类必须继承React.Component

      如果想要访问父组件传递过来的参数,可通过this.props的方式去访问

      在组件中必须实现render方法,在return中返回React对象

  • 函数组件的根基是FP,也就是函数式编程,结构化编程的一种

    通过函数编写的形式去实现一个React组件,是React中定义组件最简单的方式

    本质上最大的不同:相较于类组件,函数组件更纯粹、简单、易测试

    • 类组件在请求运行时更新。this.props 将会改变。从“最新”的 props 中读取 到最新的值

    • 函数组件,本身就不存在this,props并不发生改变,因此同样是点击,alert的内容仍旧是之前的内容

2. 状态管理:

  • 在hooks出来之前,函数组件就是无状态组件,不能保管组件的状态,不像类组件中调用setState,如果想要管理state状态,可以使用useState

  • 在使用hooks情况下,一般如果函数组件调用state,则需要创建一个类组件或者state提升到你的父组件中,然后通过props对象传递到子组件

3.生命周期:

  • 在函数组件中,并不存在生命周期,这是因为这些生命周期钩子都来自于继承的React.Component

  • 生命周期是类组件的特有能力 ,所以,如果用到生命周期,就只能使用类组件

  • 但是函数组件使用useEffect也能够完成替代生命周期的作用

4.调用方式:

  • 如果是一个函数组件,调用则是执行函数即可

  • 如果是一个类组件,则需要将组件进行实例化,然后调用实例对象的render方法

5.设计模式:类组件可以实现继承,函数组件缺少继承能力(组合优于继承)

6.性能优化:

  • 类组件优化依靠shouldComponentUpdatt函数去阻断渲染

  • 函数组件靠 React.memo来优化

在未来趋势中由于hooks的推出,以及类组件不易优化,函数组件更能适应以后的发展

5.设计模式

📌如何设计react组件(react组件的设计模式、如何将组件更好的组合)

基于场景的设计分类

哑组件、无状态组件、展示组件:只作展示、独立运行、不额外增加功能的组件
有状态组件、灵巧组件:处理业务逻辑与数据状态的组件

展示组件的复用性更强,灵巧组件更专注于业务本身

展示组件

展示组件受制于外部的props控制,具有极强的通用性,复用率很高

代理组件

展示组件中的代理组件常用于封装常用属性,减少重复代码;业务上看,代理组件隔绝Antd,仅是一个组件Props API层的交互

import { Button as AntdButton } from from 'antd'
const Button = props =>
  <AntdButton size="small" type="primary" {...props}>
export default Button

切断了外部组件库的强依赖特性,在大厂中引入外部组件库需要考虑两点:

  • 如果当前组件库不能使用了,是否能实现业务上的无痛切换;

  • 如果需要批量修改基础组件的字段,如何解决?

代理组件的设计模式很好地解决了上面两个问题。从业务上看,代理组件隔绝了 Antd,仅仅是一个组件 Props API 层的交互。这一层如若未来需要替换,是可以保证兼容、快速替换的,而不需要在原有的代码库中查找修改。其次,如果要修改基础组件的颜色、大小、间距,代理组件也可以相对优雅地解决,使得这些修改都内聚在当前的 Button 组件中,而非散落在其他地方。

样式组件

样式组件也是一种代理组件,只是又细分了处理样式领域,将当前的关注点分离到当前组件内

灵巧组件

容器组件:几乎没有复用性,主要用在拉取数据与组合组件两个方面

高阶组件:

src
├── components
│   ├── basic
│   ├── container
│   └── hoc
└── pages
  • 首先将最基本的展示组件放入 basic 目录中;

  • 然后将容器组件放入 container;

  • 高阶组件放入 hoc 中;

  • 将页面外层组件放在页面目录中;

  • 通过目录级别完成切分。

React 组件应从设计与工程实践两个方向进行探讨。

从设计上而言,社区主流分类的方案是展示组件与灵巧组件。

展示组件内部没有状态管理,仅仅用于最简单的展示表达。展示组件中最基础的一类组件称作代理组件。代理组件常用于封装常用属性、减少重复代码。很经典的场景就是引入 Antd 的 Button 时,你再自己封一层。如果未来需要替换掉 Antd 或者需要在所有的 Button 上添加一个属性,都会非常方便。基于代理组件的思想还可以继续分类,分为样式组件与布局组件两种,分别是将样式与布局内聚在自己组件内部。

灵巧组件由于面向业务,其功能更为丰富,复杂性更高,复用度低于展示组件。最经典的灵巧组件是容器组件。在开发中,我们经常会将网络请求与事件处理放在容器组件中进行。容器组件也为组合其他组件预留了一个恰当的空间。还有一类灵巧组件是高阶组件。高阶组件被 React 官方称为 React 中复用组件逻辑的高级技术,它常用于抽取公共业务逻辑或者提供某些公用能力。常用的场景包括检查登录态,或者为埋点提供封装,减少样板代码量。高阶组件可以组合完成链式调用,如果基于装饰器使用,就更为方便了。高阶组件中还有一个经典用法就是反向劫持,通过重写渲染函数的方式实现某些功能,比如场景的页面加载圈等。但高阶组件也有两个缺陷,第一个是静态方法不能被外部直接调用,需要通过向上层组件复制的方式调用,社区有提供解决方案,使用 hoist-non-react-statics 可以解决;第二个是 refs 不能透传,使用 React.forwardRef API 可以解决。

从工程实践而言,通过文件夹划分的方式切分代码。我初步常用的分割方式是将页面单独建立一个目录,将复用性略高的 components 建立一个目录,在下面分别建立 basic、container 和 hoc 三类。这样可以保证无法复用的业务逻辑代码尽量留在 Page 中,而可以抽象复用的部分放入 components 中。其中 basic 文件夹放展示组件,由于展示组件本身与业务关联性较低,所以可以使用 Storybook 进行组件的开发管理,提升项目的工程化管理能力。

高阶组件???

📌 如何在渲染劫持中为原本的渲染结果添加新的样式?

在调用 super.render 的时候就可以拿到原本的渲染结果

二、状态管理

从状态的理解运用于工程化时间入手,理解React的状态管理

📌 setState 是同步更新还是异步更新

setState用于变更状态触发组件重新渲染,更新视图U

合成事件

  • React 给 document 挂上[事件监听]

  • DOM 事件触发后冒泡到 document;

  • React 找到对应的组件,造出一个合成事件出来;

  • 并按组件树模拟一遍事件冒泡。

在react18之前

setState设置的值会在函数执行结束后再进行合并,然后再更新到页面上

想要获取到最新的值,那么需要在setState函数中添加第二个参数,这样获取到的state就是更新过后的

在原生事件addEventLinstener或者定时器中,setState是同步的,可以拿到最新的值

📌React面向组件跨层级通信

跨层级通信:一个基本,多个场景

父与子:父组件包裹子组件,父组件向子组件传递数据

  • 在初始化时展示默认文案
    初始化以后通过网络请求拉取文案数据

    通过Props传递state的文案数据更新按钮中的文案

子与父:子组件存于父组件中,子组件需向父组件传递数据

  • 主要依赖于回调函数
  • 使子组件专注业务逻辑,父组件专注渲染结果

兄弟:并列存于父组件中,需进行数据进行相互传递
无直接关系:处一棵树中相距甚远位置,但需共享、传递数据

在跨层级通信中,主要分为一层或多层的情况。

如果只有一层,那么按照 React 的树形结构进行分类的话,主要有以下三种情况:父组件向子组件通信,子组件向父组件通信以及平级的兄弟组件间互相通信。

在父与子的情况下,因为 React 的设计实际上就是传递 Props 即可。那么场景体现在容器组件与展示组件之间,通过 Props 传递 state,让展示组件受控。

在子与父的情况下,有两种方式,分别是回调函数与实例函数。回调函数,比如输入框向父级组件返回输入内容,按钮向父级组件传递点击事件等。实例函数的情况有些特别,主要是在父组件中通过 React 的 ref API 获取子组件的实例,然后是通过实例调用子组件的实例函数。这种方式在过去常见于 Modal 框的显示与隐藏。这样的代码风格有着明显的 jQuery 时代特征,在现在的 React 社区中已经很少见了,因为流行的做法是希望组件的所有能力都可以通过 Props 控制。

多层级间的数据通信,有两种情况。第一种是一个容器中包含了多层子组件,需要最底部的子组件与顶部组件进行通信。在这种情况下,如果不断透传 Props 或回调函数,不仅代码层级太深,后续也很不好维护。第二种是两个组件不相关,在整个 React 的组件树的两侧,完全不相交。那么基于多层级间的通信一般有三个方案。

第一个是使用 React 的 Context API,最常见的用途是做语言包国际化。

第二个是使用全局变量与事件。全局变量通过在 Windows 上挂载新对象的方式实现,这种方式一般用于临时存储值,这种值用于计算或者上报,缺点是渲染显示时容易引发错误。全局事件就是使用 document 的自定义事件,因为绑定事件的操作一般会放在组件的 componentDidMount 中,所以一般要求两个组件都已经在页面中加载显示,这就导致了一定的时序依赖。如果加载时机存在差异,那么很有可能导致两者都没能对应响应事件。

第三个是使用状态管理框架,比如 Flux、Redux 及 Mobx。优点是由于引入了状态管理,使得项目的开发模式与代码结构得以约束,缺点是学习成本相对较高。

📌列举一种状态管理框架

三、渲染流程

理解渲染流程做正确的性能优化,加强对React工作模式的理解程度

四、性能优化

五、React Hooks

Hooks原理 Api区别及最佳设计模式,由组件模式转向Hooks

六、React生态

React-Router及常用工具库,React生态圈

posted @   Lu西西  阅读(136)  评论(0编辑  收藏  举报
相关博文:
点击右上角即可分享
微信分享提示