个人整理前端八股文和面试题,分模块梳理
包含转载内容
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 生命周期
上文整理了17版本前后的类组件生命周期变化 以及 函数组件的抽象生命周期
MVVM概念
MVVM是一种软件架构模式,MVVM 分为 Model、View、ViewModel:
Model代表数据模型,数据和业务逻辑都在Model层中定义;View代表UI视图,负责数据的展示;ViewModel负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;
Model和View并无直接关联,而是通过ViewModel来进行联系的,Model和ViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步。
传统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(树编辑距离)
上图中,最小操作步数(编辑距离)为 3:
- 删除 ul 节点
- 添加 span 节点
- 添加 text 节点
该算法经过多年演化,最终算法复杂度为
React diff
传统diff显然无法达到高性能要求。React团队通过大胆的假设,并基于假设提出相关策略,成功的将 复杂度的问题转化为 复杂度的问题。
(1)两个假设
为了优化 diff 算法,React 提出了两个假设:
- 两个不同类型的元素会产生出不同的树
- 开发者可以通过 key prop 来暗示哪些子元素在不同的渲染下能保持稳定
(2)三个策略
基于这上述两个假设,React 针对性的提出了三个策略以对 diff 算法进行优化:
- Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
- 拥有相同类型的两个组件将会生成相似的树形结构,拥有不同类型的两个组件将会生成不同树形结构
- 对于同一层级的一组子节点,它们可以通过唯一 key 进行区分
(3)diff 具体优化
基于上述三个策略,React 分别对以下三个部分进行了 diff 算法优化
- 通过分层对比策略,对 tree diff 进行算法优化
- 通过相同类生成相似树形结构,不同类生成不同树形结构以及
shouldComponentUpdate策略,对 component diff 进行算法优化 - 通过设置唯一 key 策略,对 element diff 进行算法优化
详细优化方案可以参考上文链接
(4)如何进行 diff
React 是基于组件构建的,首先可以将整个虚拟 DOM 树,抽象为 React 组件树(每一个组件又是由一颗更小的组件树构成,依次类推),将 React diff 策略应用比较这颗组件树,若其中某个组件需要进行比较,将这个组件看成一颗较小的组件树,继续用 React diff 策略比较这颗较小的组件树,依次类推,直到层次遍历完所有的需要比较的组件。
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 Tree 和WorkInProgress Tree。其中,Fiber Tree为主,WorkInProgress Tree为辅。这两棵树互相持有引用,每一次更新的操作都是在 WorkInProgress Tree 上完成的,当更新完成后再用 WorkInProgress Tree 替换掉原有的 Fiber Tree。这样的操作:
- 复用内部对象(fiber)
- 节省内存分配、GC 的时间开销
- 即使运行中有错误,也不会影响 View 上的数据
触发更新时,Fiber执行流程如下,主要划分为调和阶段和提交阶段
React 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
1. 编程风格
React语法少,难度大;在React中的实现更加符合js的逻辑但是略显麻烦。
Vue语法多,难度小;在Vue中的实现相比于React更加简单但是没有遵循js原生的规则。
Vue 写起来更像是写 Vue 代码,React 写起来更像是写 JavaScript 代码。
2. 视图风格
Vue 采用 <template> 字符串模板。更贴近 HTML,学习成本低,但有时候不灵活。
React 采用 JSX 语法,更类似于 js ,限制比较多,(像一些关键字 class、for,单标签要闭合、属性要驼峰、组件名要大写等等这些都要注意),但是可以跟模板语法很好的进行结合。
3. 组件风格
Vue2 中采用选项式API,但是由于它不够灵活,而且 this 指向不够简单,Vue3 中给我们提供了组合式API的写法,组合式API更偏向函数式编程的方式,它的复用能力和组合的能力更强,而且没有 this 指向问题,也是 Vue 比较推荐的写法。
React 在 16.8 版本之前都是采用类组件的方式开发,类组件也会有 this 指向以及写起来很繁琐难度大的问题,在 16.8 之后 React 提供了函数组件的写法,其实函数组件和 Vue 的组合式API是很像的,它的组合和复用的能力更强,而且也没有 this 指向问题,比类组件写起来简单很多,也是 React 比较推荐的写法。
4. 路由风格
Vue 采用 Vue-Router;React 采用 React-Router
相比而言 vue 语法更加简练(useRouter useRoute),而 react 的 use 函数太多,不够统一化
5. 状态管理风格
Vue 采用 Vuex/Pinia ;React 采用 Redux/Mobx
区别:
- 语法和
API的不同:Vuex和Pinia是专门为Vue.js设计的状态管理库,因此它们的语法和API都非常类似。而Redux和Mobx可以在任何JavaScript应用程序中使用,因此它们的语法和API与特定的框架无关。 - 数据流的不同:在
Redux中,数据是通过单向数据流进行管理的,即action -> reducer -> store -> view。而在Vuex和Pinia中,数据是通过Vuex store或Pinia store直接管理的,不需要reducer。而在Mobx中,数据则是通过响应式数据实现的。 - 异步处理的不同:在
Redux中,异步处理通常需要使用中间件来处理异步操作。而在Vuex和Pinia中,异步操作可以通过actions处理。而在Mobx中,则可以使用async/await或reaction函数来处理异步操作。 - 开销和复杂性的不同:
Redux和Mobx都需要在应用程序中进行额外的设置和配置,并且在处理大量数据时可能会导致性能问题。而Vuex和Pinia的设置和配置相对简单,并且在大多数情况下可以处理大量数据。
总的来说,Vuex 和 Pinia 适用于 Vue.js 应用程序,提供了一种简单和直接的状态管理方式,而 Redux 和 Mobx 则可以在多种应用程序中使用,提供了更灵活的状态管理方案。