关于react的面试题

1. 说说你对react的理解?有哪些特性?

理解
React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案
遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效
使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流

特性

JSX语法
单向数据绑定
虚拟DOM
声明式编程
Component

特点

声明式设计
组件化
高效
灵活
Jsx语法

2. 说说Real DOM和Virtual DOM的区别?优缺点?

区别
虚拟dom不会进行重绘和回流,而真实dom会频繁重排与重绘
虚拟dom的总损耗是”虚拟dom的增删改+真实dom的差异增删改+重排“;真实dom的消耗是”真实dom全部增删改+重排“

优缺点
1.真实dom

​ 优点:
​ 1. 直接操作HTML,易用

​ 缺点:
​ 1. 解析速度慢,效率低,内存占用量高
​ 2. 性能差:频繁操作真实DOM,导致重绘、回流

2.虚拟dom

​ 优点:
​ 1. 减少真实dom的频繁更新,减少重绘回流、占用内存少

​ 2. 跨平台:一套react代码可以多端运行

​ 缺点:
​ 1. 页面首次渲染时,由于多一层虚拟dom的计算,速度比正常慢些

3. 说说React生命周期有哪些不同的阶段?每个阶段对应的方法是?

初始化阶段:

由ReactDOM.render()触发-----初次渲染
1.	constructor()
2.	componentWillMount()
3.	render()		必须的
4.	componentDidMount()   常用,一般在这个钩子做一些初始化,如:开启定时器、发送网络请求、订阅消息

更新阶段:

由组件内部this.setState()或父组件render触发
1.	shouldComponentUpdate()
2.	componentWillUpdate()
		forceUpdate()强制更新用得少
3.	render()		必须的
4.	componentDidUpdate()   

卸载阶段:

componentWillUnmount()	常用,一般在这个钩子做一些收尾的事情,如:关闭定时器、取消订阅消息

其他生命周期

componentWillReceiveProps:父组件与子组件进行数据传递时触发
getDerivedStateFromProps: static getDerivedStateFromProps(nextProps, prevState),这是个静态方法,当我们接收到新的属性想去修改state,可以使用getDerivedStateFromProps,衍生新的state,此方法在更新个挂载阶段都可能会调用
getSnapshotBeforeUpdate: getSnapshotBeforeUpdate(prevProps, prevState),这个方法在render之后,componentDidUpdate之前调用,有两个参数prevProps和prevState,表示之前的属性和之前的state,这个函数有一个返回值,会作为第三个参数传给componentDidUpdate,如果你不想要返回值,可以返回null,此生命周期必须与componentDidUpdate搭配使用

4. 说说React中setState执行机制?

一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用

在使用setState更新数据的时候,setState的更新类型分成:

  1. 同步更新:在setTimeout或者原生dom事件中,setState是同步

  2. 异步更新:在组件生命周期或React合成事件中,setState是异步

5. 说说react的事件机制?

React基于浏览器的事件机制自身实现了一套事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等

在React中这套事件机制被称之为合成事件

而合成事件是 React模拟原生 DOM事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器。根据 W3C规范来定义合成事件,兼容所有浏览器,拥有与浏览器原生事件相同的接口

执行顺序

React 所有事件都挂载在 document 对象上
当真实 DOM 元素触发事件,会冒泡到 document 对象后,再处理 React 事件
所以会先执行原生事件,然后处理 React 事件
最后真正执行 document 上挂载的事件

总结:

  1. React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
  2. React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()无效的原因。
  3. React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
  4. React 有一套自己的合成事件 SyntheticEvent

6. React组件之间如何通信?

父传子:⽗组件可以通过向⼦组件传 props 的⽅式来实现
子传父:可以采用props+回调的方式
兄弟组件通信:可以通过兄弟节点的共同父节点,由父节点转发信息,实现兄弟间通信
跨层级通信:context状态树
发布者订阅者模式:发布者发布事件,订阅者监听到事件后做出反应,可以通过引⼊ event 模块进⾏
全局状态管理工具:可以借助 Redux 或 Mobx以及react-redux 等全局状态管理⼯具进⾏通信,它们会维护⼀个全局状态中⼼(Store),并可以根据不同的事件产⽣新的状态
hooks中帮我们封装好的:useContext和useReducer

7. 说说你对受控组件和非受控组件的理解?应用场景?

受控组件
就是受我们控制的组件,组件的状态全程响应外部数据
我们在输入框输入内容的时候,会发现输入的内容并无法显示出来,也就是input标签是一个可读的状态
如果想要解除被控制,可以为input标签设置onChange事件,输入的时候触发事件函数,在函数内部实现state的更新,从而导致input框的内容页发现改变
受控组件我们一般需要初始状态和一个状态更新事件函数

非受控组件
就是不受我们控制的组件
一般情况是在初始化的时候接受外部数据,然后自己在内部存储其自身状态

应用场景
大部分时候推荐使用受控组件来实现表单,因为在受控组件中,表单数据由React组件负责处理
如果选择非受控组件的话,控制能力较弱,表单数据就由DOM本身处理,但更加方便快捷,代码量少

8. 说说你对fiber架构的理解?解决了什么问题?

React Fiber 是 Facebook 花费两年余时间对 React 做出的一个重大改变与优化,是对 React 核心算法的一次重新实现。

在react中,主要做了以下的操作:

为每个增加了优先级,优先级高的任务可以中断低优先级的任务。然后再重新,注意是重新执行优先级低的任务

增加了异步任务,调用requestIdleCallback api,浏览器空闲的时候执行

dom diff树变成了链表,一个dom对应两个fiber(一个链表),对应两个队列,这都是为找到被中断的任务,重新执行

从架构角度来看,Fiber 是对 React核心算法(即调和过程)的重写

从编码角度来看,Fiber是 React内部所定义的一种数据结构,它是 Fiber树结构的节点单位,也就是 React 16 新架构下的虚拟DOM

一个 fiber就是一个 JavaScript对象,包含了元素的信息、该元素的更新操作队列、类型

9. 说说react diff的原理是什么?

理解
跟Vue一致,React通过引入Virtual DOM的概念,极大地避免无效的Dom操作,使我们的页面的构建效率提到了极大的提升
而diff算法就是更高效地通过对比新旧Virtual DOM来找出真正的Dom变化之处
原理

react中diff算法主要遵循三个层级的策略:

tree层级
conponent 层级
element 层级

tree层级
DOM节点跨层级的操作不做优化,只会对相同层级的节点进行比较
只有删除、创建操作,没有移动操作

conponent层级
如果是同一个类的组件,则会继续往下diff运算,如果不是一个类的组件,那么直接删除这个组件下的所有子节点,创建新的

element层级
对于比较同一层级的节点们,每个节点在对应的层级用唯一的key作为标识
提供了 3 种节点操作,分别为 INSERT_MARKUP(插入)、MOVE_EXISTING (移动)和 REMOVE_NODE (删除)
通过key可以准确地发现新旧集合中的节点都是相同的节点,因此无需进行节点删除和创建,只需要将旧集合中节点的位置进行移动,更新为新集合中节点的位置

10. 说说你对redux中间件的理解?常用的中间件有哪些?实现原理?

理解
中间件(Middleware)是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的
类似于插件,可以在不影响原本功能、并且不改动原本代码的基础上,对其功能进行增强。在Redux中,中间件主要用于增强dispatch函数。
Redux中,中间件就是放在就是在dispatch过程,在分发action进行拦截处理

常用的中间件

redux-thunk:通过action返回一个函数进行异步操作
redux-promise :通过action返回一个promise进行异步处理
redux-logger:用于日志记录

实现原理
中间件本身是一个函数,该函数接收一个store参数,表示创建的仓库,该仓库并非一个完整的仓库对象,仅包含getState,dispatch。该函数运行的时间,是在仓库创建之后运行。
由于创建仓库后需要自动运行设置的中间件函数,因此,需要在创建仓库时,告诉仓库有哪些中间件
需要调用applyMiddleware函数,将函数的返回结果作为createStore的第二或第三个参数。
中间件函数必须返回一个dispatch创建函数

11. 如何使用css实现一个三角形?

1、写一个宽高为0的盒子

2、选择三角形的底边,将此边界设置为一个颜色,另外三边设置另一个颜色(或者透明色),即可显示三角效果

12. 什么是强缓存和协商缓存?

缓存:
缓存是指浏览器(客户端)在本地磁盘中队访问过的资源保存的副本文件。

强缓存:
强缓存是根据返回头中的 Expires 或者 Cache-Control 两个字段来控制的,都是表示资源的缓存有效时间。

协商缓存:
协商缓存是由服务器来确定缓存资源是否可用。 主要涉及到两对属性字段,都是成对出现的,即第一次请求的响应头带上某个字, Last-Modified 或者 Etag,则后续请求则会带上对应的请求字段 If-Modified-Since或者 If-None-Match,若响应头没有 Last-Modified 或者 Etag 字段,则请求头也不会有对应的字段。
浏览器缓存主要有一下优点:

  1. 减少重复数据请求,避免通过网络再次加载资源,节省流量。
  2. 降低服务器的压力,提升网站性能。
  3. 加快客户端加载网页的速度, 提升用户体验。

强缓存和协商缓存的区别:

  1. 如果浏览器命中强缓存,则不需要给服务器发请求;而协商缓存最终由服务器来决定是否使用缓存,即客户端与服务器之间存在一次通信。
  2. 在 chrome 中强缓存(虽然没有发出真实的 http 请求)的请求状态码返回是 200 (fromcache);而协商缓存如果命中走缓存的话,请求的状态码是 304 (not modified)。 不同浏览器的策略不同,在
    FireFox中,from cache 状态码是 304.

13. 说说React jsx转换成真实DOM的过程?

  1. 编译JSX

    首先,JSX本质其实是javascript的语法扩展,和模板语言非常接近,但是其充分具备javascript的能力。但是其要在javascript生效的话,需使用到 Babel 进行编译,JSX在被编译后,会变成一个针对 React.createElement 的调用。

  2. React.createElement 内部流程

    首先,React.createElement 会接收三个参数:
    第一个参数为 type,字符串类型,用于标识节点的类型,比如,div、p等,也可以是React组件类型或 React fragment类型。
    第二个参数为 config,这是一个对象类型的参数,组件的所有属性都会键值对的形式存储在config中。
    第三个参数为 children,这也是一个对象类型的参数,它记录的是组件标签之间嵌套的内容,也就是所谓的子节点或子元素。

    整体来说,createElement的大致流程为:
    1、二次处理key、ref、self、source四个属性值;
    2、遍历config,筛选可以提到props中的属性;
    3、将children中的子元素推入childArray数组;
    4、格式化defaultProps
    5、将以上数据作为入参,发起ReactElement的调用,最终由ReactElement返回虚拟Dom对象

  3. 最终将虚拟Dom传入ReactDom.render函数中,将其转变为真实Dom

流程
书写JSX代码 => Babel编译JSX => 编译后的JSX执行React.createElement的调用 => 传入到ReactElement方法中生成虚拟Dom => 最终返回给ReactDom.render生成真实Dom

14. 说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?

@reduxjs/toolkit
一个开箱既用的高效redux开发工具,旨在成为标准的readux逻辑开发模式

react-redux
将所有的组件分为了两大类,ui组件以及容器组件,其中容器组件包含着ui组件,容器组件负责和redux进行交互

15. React render方法的原理,在什么时候会触发?

在类组件和函数组件中,render函数的形式是不同的。

在类组件中render函数指的就是render方法;而在函数组件中,指的就是整个函数组件。

在render函数中的jsx语句会被编译成我们熟悉的js代码

在render过程中,React 将新调用的 render函数返回的树与旧版本的树进行比较,这一步是决定如何更新 DOM 的必要步骤,然后进行 diff 比较,更新 DOM树

触发机制:

类组件调用 setState 修改状态
函数组件通过useState hook修改状态。函数组件通过useState这种形式更新数据,当数组的值不发生改变了,就不会触发render

总结:

render函数里面可以编写JSX,转化成createElement这种形式,用于生成虚拟DOM,最终转化成真实DOM

在React 中,类组件只要执行了 setState 方法,就一定会触发 render 函数执行,函数组件使用useState更改状态不一定导致重新render

组件的props 改变了,不一定触发 render 函数的执行,但是如果 props 的值来自于父组件或者祖先组件的 state

在这种情况下,父组件或者祖先组件的 state 发生了改变,就会导致子组件的重新渲染

所以,一旦执行了setState就会执行render方法,useState 会判断当前值有无发生改变确定是否执行render方法,一旦父组件发生渲染,子组件也会渲染

16. React性能优化的手段有哪些?

1、使用纯组件;(PureComponent) 
2、使用 React.memo 进行组件记忆(React.memo 是一个高阶组件),对 于相同的输入,不重复执行;
3、如果是类组件,使用 shouldComponentUpdate(这是在重新渲染组件之前触发的其中一个生命周期事件)生命周期事件,可以利用此事件来决定何时需要重新渲染组件;
4、路由懒加载;
5、使用 React Fragments 避免额外标记;
6、不要使用内联函数定义(如果我们使用内联函数,则每次调用“render”函数时都会创建一个新的函数实例);
7、避免在Willxxx系列的生命周期中进行异步请求,操作dom等;
8、如果是类组件,事件函数在Constructor中绑定bind改变this指向;
9、避免使用内联样式属性;
10、优化 React 中的条件渲染;
11、不要在 render 方法中导出数据;
12、列表渲染的时候加key;
13、在函数组件中使用useCallback和useMemo来进行组件优化,依赖没有变化的话,不重复执行;
14、类组件中使用immutable对象;

17. 如何通过原生js实现一个节流函数和防抖函数?

本质上是优化高频率执行代码的一种手段

防抖
当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间(非常短的时间);
当事件密集触发时,函数的触发会被频繁的推迟;
只有等待了一段时间也没有事件触发,才会真正的执行响应函数;

应用场景:
	输入框中频繁的输入内容,搜索或者提交信息;
	频繁的点击按钮,触发某个事件;
	监听浏览器滚动事件,完成某些特定操作;
	用户缩放浏览器的resize事件;

节流
当事件触发时,会执行这个事件的响应函数;
如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函;
不管在这个中间有多少次触发这个事件,执行函数的频繁总是固的;

应用场景:
	游戏中的一些设计--王者荣耀 英雄的普攻; 
	监听页面的滚动事件;
	鼠标移动事件; 				
	用户频繁点击按钮操作;

18. 说说webpack中常见的loader?解决了什么问题?

webpack是属于模块化方案,他能让任意类型的文件都能运行在浏览器中,怎么做到呢?这时就有了loader
loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。
特性:

•	loader 可以是同步的,也可以是异步的
•	loader 运行在 Node.js 中,并且能够执行任何操作
•	除了常见的通过 package.json 的 main 来将一个 npm 模块导出为 loader,还可以在 module.rules 中使用 loader 字段直接引用一个模块
•	插件(plugin)可以为 loader 带来更多特性
•	loader 能够产生额外的任意文件

可以通过 loader 的预处理函数,为 JavaScript 生态系统提供更多能力。用户现在可以更加灵活地引入细粒度逻辑,例如:压缩、打包、语言翻译和更多其他特性

常见的loader:

•	style-loader 将css添加到DOM的内联样式标签style里
•	css-loader 允许将css文件通过require的方式引入,并返回css代码
•	less-loader 处理less
•	sass-loader 处理sass
•	postcss-loader 用postcss来处理CSS
•	autoprefixer-loader 处理CSS3属性前缀,已被弃用,建议直接使用postcss
•	file-loader 分发文件到output目录并返回相对路径
•	url-loader 和file-loader类似,但是当文件小于设定的limit时可以返回一个Data Url
•	html-minify-loader 压缩HTML
•	babel-loader 用babel来转换ES6文件到ES5

19. 说说如何借助webpack来优化前端性能?

随着前端的项目逐渐扩大,必然会带来的一个问题就是性能
尤其在大型复杂的项目中,前端业务可能因为一个小小的数据依赖,导致整个页面卡顿甚至奔溃
一般项目在完成后,会通过webpack进行打包,利用webpack对前端项目性能优化是一个十分重要的环节
通过webpack优化前端的手段有:

•	JS代码压缩
•	CSS代码压缩
•	Html文件代码压缩
•	文件大小压缩
•	图片压缩
•	Tree Shaking,Tree Shaking 是一个术语,在计算机中表示消除死代码
•	代码分离
•	内联 chunk

关于webpack对前端性能的优化,可以通过文件体积大小入手,其次还可通过分包的形式、减少http请求次数等方式,实现对前端性能的优化

20. 说说javascript内存泄漏的几种情况?

内存泄漏(Memory leak)是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存

并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费

程序的运行需要内存。只要程序提出要求,操作系统或者运行时就必须供给内存

对于持续运行的服务进程,必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃

垃圾回收机制
Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存

原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存

通常情况下有两种实现方式:

  1. 标记清除
  2. 引用计数

常见的情况有:

  1. 意外的全局变量
  2. 闭包引起的内存泄漏
  3. 没有清理对DOM元素的引用
  4. 被遗漏的定时器和回调函数
  5. 使用事件监听addEventListener监听的时候,在不监听的情况下没有使用removeEventListener取消对事件监听
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值