关于react的面试题(三)
- 1. shouldComponentUpdate有什么作用?
- 2. Vue中自定义指令的理解,应用场景有哪些?
- 3. 说说Connect组件的原理是什么?
- 4. 说说javascript内存泄漏的几种情况?
- 5. 大文件如何做断点续传?
- 6. 原生js如何实现上拉加载下拉刷新?
- 7. 说说设备像素、css像素、设备独立像素、dpr、ppi之间的区别?
- 8. 谈谈你对BFC的理解?
- 9. 说说TCP为什么需要三次握手和四次握手?
- 10. react新出来两个钩子函数是什么?和删掉的will系列有什么区别?
- 11. 最少说出三种前端清除浮动的方法?
- 12. 什么是强缓存和协商缓存?
- 13. 说说React jsx转换成真实DOM的过程?
- 14. 说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?
- 15. React render方法的原理,在什么时候会触发?
- 16. React性能优化的手段有哪些?
- 17. React组件之间如何通信?
- 18. 说说你对git rebase 和git merge的理解?区别?
- 19. 在使用redux过程中,如何防止定义的action-type的常量重复?
- 20. 调和阶段setState干了什么?
1. shouldComponentUpdate有什么作用?
shouldComponentUpdate询问组件是否需要更新的一个钩子函数,判断数据是否需要重新渲染,返回一个布尔值。默认的返回值是true,需要重新render()。若如果返回值是false则不触发渲染,利用这个生命周期函数可以强制关闭不需要更新的子组件来提升渲染性能。
这个方法用来判断是否需要调用 render 方法重新描绘 dom。
因为 dom 的描绘非常消耗性能,如果我们能在 shouldComponentUpdate 方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。
2. Vue中自定义指令的理解,应用场景有哪些?
v-text 渲染内容到节点中,不解析html
v-html 渲染内容到绑定的节点中,解析html
v-if 通过条件判断节点是否显示
v-show 通过条件判断节点是否显示
v-on 绑定事件,可以简写为@
v-bind 绑定属性 可以简写:
v-for 循环列表指令
v-model 表单双向绑定指令
3. 说说Connect组件的原理是什么?
首先connect之所以会成功,是因为Provider组件:
在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
接收Redux的store作为props,通过context对象传递给子孙组件上的connect
那connect做了些什么呢?
它真正连接 Redux 和 React,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
4. 说说javascript内存泄漏的几种情况?
**内存泄漏(Memory leak)**是在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存
并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费
程序的运行需要内存。只要程序提出要求,操作系统或者运行时就必须供给内存
对于持续运行的服务进程,必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃
垃圾回收机制
Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存
原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存
通常情况下有两种实现方式:
- List item
- 引用计数
常见的情况有:
- 意外的全局变量
- 闭包引起的内存泄漏
- 没有清理对DOM元素的引用
- 被遗漏的定时器和回调函数
- 使用事件监听addEventListener监听的时候,在不监听的情况下没有使用removeEventListener取消对事件监听
5. 大文件如何做断点续传?
是什么?不管怎样简单的需求,在量级达到一定层次时,都会变得异常复杂
文件上传简单,文件变大就复杂,上传大文件时,会影响我们的用户体验
上传时间会变长,高频次文件上传失败,失败后又需要重新上传等等
为了解决上述问题,我们需要对大文件上传单独处理
这里涉及到分片上传及断点续传两个概念
分片上传
分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(Part)来进行分片上传
断点续传
断点续传指的是在下载或上传时,将下载或上传任务人为的划分为几个部分
实现思路
整体思路比较简单,拿到文件,保存文件唯一性标识,切割文件,分段上传,每次上传一段,根据唯一性标识判断文件上传进度,直到文件的全部片段上传完毕
6. 原生js如何实现上拉加载下拉刷新?
下拉刷新和上拉加载这两种交互方式通常出现在移动端中本质上等同于PC网页中的分页,只是交互形式不同
上拉加载的本质是页面触底,或者快要触底时的动作
scrollTop
:滚动视窗的高度距离window
顶部的距离,它会随着往上滚动而不断增加,初始值是0,它是一个变化的值clientHeight
:它是一个定值,表示屏幕可视区域的高度;scrollHeight
:页面不能滚动时也是存在的,此时scrollHeight等于clientHeight。scrollHeight表示body
所有元素的总长度(包括body元素自身的padding)
下拉刷新
下拉刷新的本质是页面本身置于顶部时,用户下拉时需要触发的动作
- 监听原生
touchstart
事件,记录其初始位置的值,e.touches[0].pageY
; - 监听原生
touchmove
事件,记录并计算当前滑动的位置值与初始位置值的差值,大于0
表示向下拉动,并借助CSS3的translateY
属性使元素跟随手势向下滑动对应的差值,同时也应设置一个允许滑动的最大值; - 监听原生
touchend
事件,若此时元素滑动达到最大值,则触发callback
,同时将translateY
重设为0
,元素回到初始位置
7. 说说设备像素、css像素、设备独立像素、dpr、ppi之间的区别?
设备像素(device pixels),又称为物理像素指设备能控制显示的最小物理单位,不一定是一个小正方形区块,也没有标准的宽高,只是用于显示丰富色彩的一个“点”而已
CSS像素: 适用于web编程,在 CSS 中以 px 为后缀,是一个长度单位
设备独立像素(Device Independent Pixel):与设备无关的逻辑像素,代表可以通过程序控制使用的虚拟像素,是一个总体概念,包括了CSS像素
dpr(device pixel ratio),设备像素比,代表设备独立像素到设备像素的转换关系
ppi (pixel per inch),每英寸像素,表示每英寸所包含的像素点数目,更确切的说法应该是像素密度。数值越高,说明屏幕能以更高密度显示图像
8. 谈谈你对BFC的理解?
BFC
(Block Formatting Context),即块级格式化上下文,它是页面中的一块渲染区域,并且有一套属于自己的渲染规则
BFC
目的是形成一个相对于外界完全独立的空间,让内部的子元素不会影响到外部的元素
触发条件
- 根元素,即HTML元素
- 浮动元素:float值为left、right
- overflow值不为 visible,为 auto、scroll、hidden
- display的值为inline-block、inltable-cell、table-caption、table、inline-table、flex、inline-flex、grid、inline-grid
- position的值为absolute或fixed
9. 说说TCP为什么需要三次握手和四次握手?
三次握手(Three-way Handshake)其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包,主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。
tcp终止一个连接,需要经过四次挥手
服务端在收到客户端断开连接Fin报文后,并不会立即关闭连接,而是先发送一个ACK包先告诉客户端收到关闭连接的请求,只有当服务器的所有报文发送完毕之后,才发送FIN报文断开连接,因此需要四次挥手
10. react新出来两个钩子函数是什么?和删掉的will系列有什么区别?
React16废弃的生命周期有3个will:
componentWillMount
componentWillReceiveProps
componentWillUpdate
废弃的原因: 是在React16的Fiber架构中,调和过程会多次执行will周期,不再是一次执行,失去了原有的意义。此外,多次执行,在周期中如果有setState或dom操作,会触发多次重绘,影响性能,也会导致数据错乱。
React16的2个新的生命周期:
getDerivedStateFromProps,getSnapshotBeforeUpdate
getDerivedStateFromProps的用法
触发时机频繁,16.3是在props变化时触发,16.4则改为在每次组件渲染器调用,即无论props变化,组件自己setState,父组件render 都会触发
静态方法,本意是隔离访问组件实例,却造成访问组件的数据和方法十分不便,难以进行数据比较
不能setState,而是返回一个对象来更新state,使用不便,也可能触发多次更新状态
当组件实例化的时候,这个方法替代了componentWillMount(),而当接收到新的 props 时,该方法替代了 componentWillReceiveProps() 和 componentWillUpdate()
getSnapshotBeforeUpdate的用法
在render之后,更新dom之前,state已更新。可以用来读取dom,强制用户只能在mount阶段读取dom。
getSnapshotBeforeUpdate这个周期在Fiber架构中,只会调用一次。
11. 最少说出三种前端清除浮动的方法?
1.使用空标签清除浮动clear:both。
原理:添加一个空div,利用css提高的clear:both清除浮动,让父级div能自动获取到高度
优点:通俗易懂,容易掌握
缺点:会添加很多无意义的空标签,有违结构与表现的分离,在后期维护中将是噩梦
建议:不推荐使用,但此方法是以前主要使用的一种清除浮动方法
2.父级div定义overflow:hidden
原理:必须定义width或zoom:1,同时不能定义height,使用overflow:hidden时,浏览器会自动检查浮动区域的高度
优点:简单,代码少,浏览器支持好
缺点:不能和position配合使用,因为超出的尺寸的会被隐藏
建议:只推荐没有使用position或对overflow:hidden理解比较深的朋友使用
3、父级div定义伪类:after和zoom(用于非IE浏览器)
原理:IE8以上和非IE浏览器才支持:after,原理和方法1有点类似,zoom(IE转有属性)可解决ie6,ie7浮动问题
优点:浏览器支持好,不容易出现怪问题(目前:大型网站都有使用,如:腾迅,网易,新浪等等)。
缺点:代码多,要两句代码结合使用,才能让主流浏览器都支持
建议:推荐使用,建议定义公共类,以减少CSS代码
display:block 使生成的元素以块级元素显示,占满剩余空间;
height:0 避免生成内容破坏原有布局的高度。
visibility:hidden 使生成的内容不可见,并允许可能被生成内容盖住的内容可以进行点击和交互;
通过 content:".“生成内容作为最后一个元素,至于content里面是点还是其他都是可以的,例如oocss里面就有经典的 content:“XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”,有些版本可能content 里面内容为空,不推荐这样做的,firefox直到7.0 content:”” 仍然会产生额外的空隙;
zoom:1 触发IE hasLayout。
通过分析发现,除了clear:both用来清除浮动的,其他代码无非都是为了隐藏掉content生成的内容,这也就是其他版本的闭合浮动为什么会有font-size:0,line-height:0。
4、父级div定义height
原理:父级div手动定义height,就解决了父级div无法自动获取到高度的问题。
优点:简单,代码少,容易掌握
缺点:只适合高度固定的布局,要给出精确的高度,如果高度和父级div不一样时,会产生问题
5、父级div定义overflow:auto
原理:必须定义width或zoom:1,同时不能定义height,使用overflow:auto时,浏览器会自动检查浮动区域的高度
优点:简单,代码少,浏览器支持好
缺点:内部宽高超过父级div时,会出现滚动条。
建议:不推荐使用,如果你需要出现滚动条或者确保你的代码不会出现滚动条就使用吧。
12. 什么是强缓存和协商缓存?
缓存:
缓存是指浏览器(客户端)在本地磁盘中队访问过的资源保存的副本文件。
强缓存:
强缓存是根据返回头中的 Expires 或者 Cache-Control 两个字段来控制的,都是表示资源的缓存有效时间。
协商缓存:
协商缓存是由服务器来确定缓存资源是否可用。 主要涉及到两对属性字段,都是成对出现的,即第一次请求的响应头带上某个字, Last-Modified 或者 Etag,则后续请求则会带上对应的请求字段 If-Modified-Since或者 If-None-Match,若响应头没有 Last-Modified 或者 Etag 字段,则请求头也不会有对应的字段。
浏览器缓存主要有一下优点:
- 减少重复数据请求,避免通过网络再次加载资源,节省流量。
- 降低服务器的压力,提升网站性能。
- 加快客户端加载网页的速度, 提升用户体验。
强缓存和协商缓存的区别:
- 如果浏览器命中强缓存,则不需要给服务器发请求;而协商缓存最终由服务器来决定是否使用缓存,即客户端与服务器之间存在一次通信。
- 在 chrome 中强缓存(虽然没有发出真实的 http 请求)的请求状态码返回是 200 (fromcache);而协商缓存如果命中走缓存的话,请求的状态码是 304 (not modified)。 不同浏览器的策略不同,在
FireFox中,from cache 状态码是 304.
13. 说说React jsx转换成真实DOM的过程?
使用react.createElement或者JSX编写react组件,实际上所有的JSX代码都会转换成react.createElement内容,babel帮助我们完成了转换的过程,createElement函数对key和ref等特殊的props进行了处理,并获取defaultProps对默认的props进行赋值,并且对传入的子节点进行处理,最终构造成一个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. React组件之间如何通信?
父传子:⽗组件可以通过向⼦组件传 props 的⽅式来实现
子传父:可以采用props+回调的方式
兄弟组件通信:可以通过兄弟节点的共同父节点,由父节点转发信息,实现兄弟间通信
跨层级通信:context状态树
发布者订阅者模式:发布者发布事件,订阅者监听到事件后做出反应,可以通过引⼊ event 模块进⾏
全局状态管理工具:可以借助 Redux 或 Mobx以及react-redux 等全局状态管理⼯具进⾏通信,它们会维护⼀个全局状态中⼼(Store),并可以根据不同的事件产⽣新的状态
hooks中帮我们封装好的:useContext和useReducer
18. 说说你对git rebase 和git merge的理解?区别?
有人会说Merge更好,因为它保留了最完整的工作历史
其他人则认为,Rebase变得更整洁,这使审阅者的生活更轻松,更高效。
从根本上讲,合并和rebase提供了相同的目的,以将来自一个分支(有时倍数分支)的变化集成到另一个分支中。
最常用的是在打开Pull请求之前将最新的Master或开发分支集成。虽然目的是相同的,但Merge和Rebase达到的方式不同。
rebase会把当前分支的 commit 放到公共分支的最后面,所以叫变基。就好像从公共分支又重新拉出来这个分支一样。
merge 操作遇到冲突的时候,当前merge不能继续进行下去。手动修改冲突内容后,add 修改,commit 就可以继续往下操作,
而rebase 操作的话,会中断rebase,同时会提示去解决冲突。解决冲突(vi 或则其他工具)后, 将修改add后执行git rebase —continue 继续操作(会要求写入comment),或者git rebase —skip忽略冲突,之后push到远端。
Merge具有更高的可追溯性,而Rebase则更整洁且易于审核。
19. 在使用redux过程中,如何防止定义的action-type的常量重复?
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。 Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象 Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
20. 调和阶段setState干了什么?
(1)代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。
(2)经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面;
(3)在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染;
(4)在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。