前端八股文整理 —— React

3,283 阅读12分钟

个人整理前端八股文和面试题,分模块梳理

包含转载内容

React 理解

是什么

React,用于构建用户界面的 JavaScript 库,只提供了 UI 层面的解决方案

遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效

使用虚拟DOM 来有效地操作 DOM,遵循从高阶组件到低阶组件的单向数据流

帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面

react 类组件使用一个名为 render() 的方法或者函数组件return,接收输入的数据并返回需要展示的内容

被传入的数据可在组件中通过 this.props 在 render() 访问

特性

React特性有很多,其主要特性如下所示:

  • JSX 语法
  • 单向数据绑定
  • 虚拟 DOM
  • 声明式编程
  • 组件化

虚拟DOM

是什么

Real DOM,真实DOM,意思为文档对象模型,是一个结构化文本的抽象,在页面渲染出的每一个结点都是一个真实DOM结构。

Virtual Dom,本质上是以 JavaScript 对象形式存在的对 DOM 的描述。

创建虚拟DOM目的就是为了更好将虚拟的节点渲染到页面视图中,虚拟DOM对象的节点与真实DOM的属性一一照应

区别

虚拟 DOM 不会进行排版与重绘操作,而真实 DOM 会频繁重排与重绘。

虚拟 DOM 的总损耗是 “虚拟 DOM 增删改+真实 DOM 差异增删改+排版与重绘” ,真实 DOM 的总损耗是 “真实 DOM 完全增删改+排版与重绘”

优缺点

Real DOM

优点:

  • 易用

缺点:

  • 效率低,解析速度慢,内存占用量过高
  • 性能差:频繁操作真实 DOM,易于导致重绘与回流

Virtual DOM

优点:

  • 简单方便:如果使用手动操作真实 DOM来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难
  • 性能方面:使用 Virtual DOM,能够有效避免真实 DOM 数频繁更新,减少多次引起重绘与回流,提高性能
  • 跨平台:React 借助虚拟 DOM,带来了跨平台的能力,一套代码多端运行

缺点:

  • 在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化
  • 首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,速度比正常稍慢

React 生命周期

React生命周期(类组件与函数组件)

上文整理了17版本前后的类组件生命周期变化 以及 函数组件的抽象生命周期

MVVM概念

MVVM是一种软件架构模式,MVVM 分为 ModelViewViewModel

  • Model代表数据模型,数据和业务逻辑都在Model层中定义;
  • View代表UI视图,负责数据的展示;
  • ViewModel负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;

ModelView并无直接关联,而是通过ViewModel来进行联系的,ModelViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步。

传统diff & React diff

深入理解React: diff算法

讨论的对象为React16前的React diff算法。在React 16之后,由于引入了全新的架构fiber,对应的diff算法也有改变。

是什么

React通过引入虚拟DOM(Virtual DOM)的方式,避免真实DOM的无效操作,提高页面构建效率。

React 在内存中维护一颗虚拟 DOM 树,当数据发生改变时(state & props),会自动的更新虚拟 DOM,获得一个新的虚拟 DOM 树,然后通过 Diff 算法,比较新旧虚拟 DOM 树,找出最小的有变化的部分,将这个变化的部分(Patch)加入队列,最终批量的更新这些 Patch 到实际的 DOM 中。

传统 diff

将一颗 Tree 通过最小操作步数映射为另一颗 Tree,这种算法称之为 Tree Edit Distance(树编辑距离) 传统diff.png 上图中,最小操作步数(编辑距离)为 3:

  1. 删除 ul 节点
  2. 添加 span 节点
  3. 添加 text 节点

该算法经过多年演化,最终算法复杂度为 O(n3)O(n^3)

React diff

传统diff显然无法达到高性能要求。React团队通过大胆的假设,并基于假设提出相关策略,成功的将 O(n3)O(n^3) 复杂度的问题转化为 O(n)O(n) 复杂度的问题。

(1)两个假设

为了优化 diff 算法,React 提出了两个假设:

  1. 两个不同类型的元素会产生出不同的树
  2. 开发者可以通过 key prop 来暗示哪些子元素在不同的渲染下能保持稳定

(2)三个策略

基于这上述两个假设,React 针对性的提出了三个策略以对 diff 算法进行优化:

  1. Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
  2. 拥有相同类型的两个组件将会生成相似的树形结构,拥有不同类型的两个组件将会生成不同树形结构
  3. 对于同一层级的一组子节点,它们可以通过唯一 key 进行区分

(3)diff 具体优化

基于上述三个策略,React 分别对以下三个部分进行了 diff 算法优化

  • 通过分层对比策略,对 tree diff 进行算法优化
  • 通过相同类生成相似树形结构,不同类生成不同树形结构以及shouldComponentUpdate策略,对 component diff 进行算法优化
  • 通过设置唯一 key 策略,对 element diff 进行算法优化

详细优化方案可以参考上文链接

(4)如何进行 diff

React 是基于组件构建的,首先可以将整个虚拟 DOM 树,抽象为 React 组件树(每一个组件又是由一颗更小的组件树构成,依次类推),将 React diff 策略应用比较这颗组件树,若其中某个组件需要进行比较,将这个组件看成一颗较小的组件树,继续用 React diff 策略比较这颗较小的组件树,依次类推,直到层次遍历完所有的需要比较的组件。

React Fiber

React Fiber 详细解析

Fiber产生原因

解决渲染复杂组件时严重影响用户和浏览器交互的问题。

Fiber将渲染工作分割成块并且分配到不同的帧当中去。

Fiber具体工作

React 在 render 第一次渲染时,会通过 React.createElement 创建一颗 Element 树,可以称之为 Virtual DOM Tree。 同时也会基于 Virtual DOM Tree 构建一个“结构相同” Fiber Tree

Fiber Node是构成Fiber Tree的基本单元,也是Fiber中的一个工作单元。

每个 Fiber Node 都会有一个 ExpirationTime 到期时间来确定当前时间片下是否执行该节点的更新任务。它是以任务什么时候该执行完为描述信息的,到期时间越短,则代表优先级越高。

在渲染过程中,Fiber维护两棵树,Fiber TreeWorkInProgress Tree。其中,Fiber Tree为主,WorkInProgress Tree为辅。这两棵树互相持有引用,每一次更新的操作都是在 WorkInProgress Tree 上完成的,当更新完成后再用 WorkInProgress Tree 替换掉原有的 Fiber Tree。这样的操作:

  1. 复用内部对象(fiber)
  2. 节省内存分配、GC 的时间开销
  3. 即使运行中有错误,也不会影响 View 上的数据

触发更新时,Fiber执行流程如下,主要划分为调和阶段和提交阶段

Fiber执行流程.png

React SSR

react ssr 搭建

手把手从零实现SSR

CSR

页面内容的渲染来自于服务端返回的js脚本和ajax请求的数据的执行。csr的出现实现了前后端架构分离。csr的本质就是拿到html后请求打包工具打包生成的js脚本,然后执行该脚本在渲染。缺点:不利于seo和首次渲染时间长

SSR

在服务端完成页面模板、数据预取、填充,并且在服务端就可以将完整的 HTML 内容返回给浏览器。ssr本质上就是把原来csr的初始化数据这个操作让服务端执行了,然后调用renderToString函数生成html文本,再执行服务生成一个初始的静态页面。缺点: 会增加项目整体复杂度,对服务器压力大

NSR

这是一种在 hybrid 中特有的渲染技术。简单说就是通过 Native 渲染生成 HTML 数据,并且缓存在客户端。这样一来,对于一个 hybrid WebView 的用户访问,会优先从离线包中加载离线页面模板,再通过前端 Ajax/或客户端能力请求数据,最终完成页面完整的展示

这样做的好处显而易见:我们将服务器的渲染工作放在了一个个独立的移动设备中,并借助离线存储技术,实现了页面的预加载,同时又不会增加额外的服务器压力。

ESR

边缘渲染则更加激进。ESR 其实借助了最近几年较火的“边缘计算”能力。

边缘计算: 是指在靠近物或数据源头的一侧,采用网络、计算、存储、应用核心能力为一体的开放平台,就近提供最近端服务。其应用程序在边缘侧发起,产生更快的网络服务响应,满足行业在实时业务、应用智能、安全与隐私保护等方面的基本需求。边缘计算处于物理实体和工业连接之间,或处于物理实体的顶端。而云端计算,仍然可以访问边缘计算的历史数据。

ESR 渲染利用了 CDN 能力。ESR会在 CDN 上缓存页面的静态部分,这样在用户访问页面时,可以快速返回给用户静态内容,同时在 CDN 节点上也发起动态部分内容请求,在动态内容获取之后,利用流的方式,继续返回给用户

SSR 搭建

  • 在服务端中导入要渲染的组件,可以通过调用react-dom/server包中提供renderToString来将组件渲染内容输出为字符串后返回客户端,但是只能输出静态内容,要让页面支持交互需要搭配 hydrate 使用。

  • 实现 SSR 时服务端需要支持 jsx 语法的解析,因为服务端也需要读取组件。

  • hydrate 会检查服务端与客户端的内容是否匹配。

  • 要实现动态数据需要在客户端与服务端之间做好如何使用初始 props 的约定。

React 和 Vue

React 18 与 Vue3 详细对比

1. 编程风格

React语法少,难度大;在React中的实现更加符合js的逻辑但是略显麻烦。

Vue语法多,难度小;在Vue中的实现相比于React更加简单但是没有遵循js原生的规则。

Vue 写起来更像是写 Vue 代码,React 写起来更像是写 JavaScript 代码。

2. 视图风格

Vue 采用 <template> 字符串模板。更贴近 HTML,学习成本低,但有时候不灵活。

React 采用 JSX 语法,更类似于 js ,限制比较多,(像一些关键字 classfor,单标签要闭合、属性要驼峰、组件名要大写等等这些都要注意),但是可以跟模板语法很好的进行结合。

3. 组件风格

Vue2 中采用选项式API,但是由于它不够灵活,而且 this 指向不够简单,Vue3 中给我们提供了组合式API的写法,组合式API更偏向函数式编程的方式,它的复用能力和组合的能力更强,而且没有 this 指向问题,也是 Vue 比较推荐的写法。

React16.8 版本之前都是采用类组件的方式开发,类组件也会有 this 指向以及写起来很繁琐难度大的问题,在 16.8 之后 React 提供了函数组件的写法,其实函数组件和 Vue 的组合式API是很像的,它的组合和复用的能力更强,而且也没有 this 指向问题,比类组件写起来简单很多,也是 React 比较推荐的写法。

4. 路由风格

Vue 采用 Vue-Router;React 采用 React-Router

相比而言 vue 语法更加简练(useRouter useRoute),而 react 的 use 函数太多,不够统一化

5. 状态管理风格

Vue 采用 Vuex/PiniaReact 采用 Redux/Mobx

区别:

  1. 语法和 API 的不同:VuexPinia 是专门为 Vue.js 设计的状态管理库,因此它们的语法和API都非常类似。而 ReduxMobx 可以在任何 JavaScript 应用程序中使用,因此它们的语法和API与特定的框架无关。
  2. 数据流的不同:在 Redux 中,数据是通过单向数据流进行管理的,即 action -> reducer -> store -> view。而在 VuexPinia 中,数据是通过 Vuex storePinia store 直接管理的,不需要 reducer。而在 Mobx 中,数据则是通过响应式数据实现的。
  3. 异步处理的不同:在 Redux 中,异步处理通常需要使用中间件来处理异步操作。而在 VuexPinia 中,异步操作可以通过 actions 处理。而在 Mobx 中,则可以使用 async/awaitreaction 函数来处理异步操作。
  4. 开销和复杂性的不同:ReduxMobx 都需要在应用程序中进行额外的设置和配置,并且在处理大量数据时可能会导致性能问题。而 VuexPinia 的设置和配置相对简单,并且在大多数情况下可以处理大量数据。

总的来说,Vuex 和 Pinia 适用于 Vue.js 应用程序,提供了一种简单和直接的状态管理方式,而 Redux 和 Mobx 则可以在多种应用程序中使用,提供了更灵活的状态管理方案

参考链接

前端铜九铁十必备八股文