面试题练习第三篇
- 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:
这是React
组件的钩子函数之一,该函数会在组件重新渲染之前调用,由函数的返回的boolean值
决定是否重新渲染组件。
shouldComponentUpdate
询问组件是否需要更新的一个钩子函数,判断数据是否需要重新渲染,返回一个布尔值。默认的返回值是true
,需要重新render()
。若如果返回值是false
则不触发渲染,利用这个生命周期函数可以强制关闭不需要更新的子组件来提升渲染性能。
这个方法用来判断是否需要调用 render
方法重新描绘 dom
。
因为 dom
的描绘非常消耗性能,如果我们能在 shouldComponentUpdate
方法中能够写出更优化的 dom diff
算法,可以极大的提高性能。
2. Vue中自定义指令的理解,应用场景有哪些?
自定义指令也像组件那样存在钩子函数:
bind:
只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
inserted:
被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
update:
所在组件的 VNode
更新时调用,但是可能发生在其子 VNode
更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
componentUpdated:
指令所在组件的 VNode
及其子 VNode
全部更新后调用
unbind:
只调用一次,指令与元素解绑时调用
所有的钩子函数的参数都有以下:
el:
指令所绑定的元素,可以用来直接操作 DOM
binding:
一个对象,包含以下 property
:
name:
指令名,不包括 v- 前缀。value:
指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。oldValue:
指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。expression:
字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。arg:
传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
。modifiers:
一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
vnode:
Vue
编译生成的虚拟节点
oldVnode:
上一个虚拟节点,仅在 update
和 componentUpdated
钩子中可用
除了
el
之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的dataset
来进行
应用场景
- 防抖
- 图片懒加载
- 一键
Copy
的功能
3. 说说Connect组件的原理是什么?
connect
是一个高阶函数,它真正连接 Redux
和 React
,包在我们的容器组件的外一层,接收上面 Provider
提供的 store
里面的 state
和 dispatch
,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
原理:
首先传入mapStateToProps
、mapDispatchToProps
,然后返回一个生产Component
的函数(wrapWithConnect
),然后再将真正的Component
作为参数传入wrapWithConnect
,这样就生产出一个经过包裹的Connect
组件,该组件具有:
(1)通过props.store
获取祖先Component
的store
(2)props
包括stateProps
、dispatchProps
、parentProps
,合并在一起得到nextState
,作为props
传给真正的Component
(3)componentDidMount
时,添加事件this.store.subscribe(this.handleChange)
,实现页面交互
(4)shouldComponentUpdate
时判断是否有避免进行渲染,提升页面性能,并得到nextState
(5)componentWillUnmount
时移除注册的事件this.handleChange
4. 说说javascript内存泄漏的几种情况?
内存泄漏: 由于疏忽或错误造成程序未能释放已经不再使用的内存
- 意外使用全局变量。
- 定时器,定时器不清除,一直占用内存,得不到释放,或者在定时器内调用外部函数,得不到释放。
- 闭包,内部函数引用外部函数变量,得不到释放。
- 不清理
dom
元素的引用。 - 事件监听 , 监听事件的解除,监听的时候
addEventListener
,在不监听的时候要使用remveEventListener
。
5. 大文件如何做断点续传?
断点续传 指的是在下载或上传时,将下载或上传任务人为的划分为几个部分
每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。用户可以节省时间,提高速度
一般实现方式有两种:
- 服务器端返回,告知从哪开始
- 浏览器端自行处理
上传过程中将文件在服务器写为临时文件,等全部写完了(文件上传完),将此临时文件重命名为正式文件即可
如果中途上传中断过,下次上传的时候根据当前临时文件大小,作为在客户端读取文件的偏移量,从此位置继续读取文件数据块,上传到服务器从此偏移量继续写入文件即可
实现思路
整体思路比较简单,拿到文件,保存文件唯一性标识,切割文件,分段上传,每次上传一段,根据唯一性标识判断文件上传进度,直到文件的全部片段上传完毕
6. 原生js如何实现上拉加载下拉刷新?
1.上拉加载
使用场景: 移动端历史日志、内容列表等模块中使用
简述: 当列表滑动到底部时,再做请求加载下一页列表,有利于减少http请求和浏览器渲染压力,提高页面首次加载速度
原理:
获取滑动内容外部盒子高度:
document.querySelector(“.container”).clientHeight
获取整个内容的高度:
document.querySelector(“.ul”).scrollHeight
获取卷曲出去的高度:
let tops = document.querySelector(“.container”).scrollTop
外部盒子高度+卷曲出去的高度>=内容的高度 - 离底部距离高度(自定义)
注意: 监听滑动事件scroll
当满足以上条件时执行异步请求,这里需要加一个节流,当触发是禁止再次触发,等到异步事件加载完毕后,才允许再次上拉触发。
2.下拉刷新
简述: 向下滑动页面刷新数据
原理:
监听touchstart
、touchmove
、touchend
三个事件
- 通过
touchstart
记录手指触摸时位置 - 通过
touchmove
记录向下滑动的距离,同时通过设置transform:translateY(x)
来设置内容向下移动,控制滑动距离到达某个值时,禁止页面再做滑动 - 通过
touchend
记录手指离开事件,对滑动距离达到限定值时,做刷新请求数据处理,未到限定值时自动回弹translateY(0px)
,并添加过渡动画
7. 说说设备像素、css像素、设备独立像素、dpr、ppi之间的区别?
- 无缩放情况下,1个
CSS
像素等于1个设备独立像素 - 设备像素由屏幕生产之后就不发生改变,而设备独立像素是一个虚拟单位会发生改变
PC
端中,1个设备独立像素 = 1个设备像素 (在100%
,未缩放的情况下)- 在移动端中,标准屏幕(
160ppi
)下 1个设备独立像素 = 1个设备像素 - 设备像素比(
dpr
) =设备像素 / 设备独立像素
- 每英寸像素(
ppi
),值越大,图像越清晰
8. 谈谈你对BFC的理解?
BFC
(Block Formatting Context
)块格式化上下文, 是Web
页面的可视CSS
渲染的一部分,是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。
简单的理解: BFC
就是一个块级容器,它会隔离外部,让盒子里面的元素不影响外面的元素,也就是在搭建页面的时候,不影响外面的布局。
9. 说说TCP为什么需要三次握手和四次握手?
三次握手
三次握手只是tcp
中的约束,原因为了解决三个问题,初始化 Sockets
、窗口大小 、初始序列号 并建立 TCP 连接 。三次握手最终目的是保证发送方和接收方的请求都有来有回,即:也发送出去了,也接收到回复了。
三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。
- 阻止重复历史连接的初始化;
- 对通信双方的初始序列号进行初始化;
- 讨论其他次数握手建立连接的可能性;
这三个论点才是讨论为何需要三次握手的最主要原因,其他都是次要原因。
四次挥手
- 关闭连接时,客户端向服务端发送
FIN
时,仅仅表示客户端不再发送数据了但是还能接收数据。 - 服务器收到客户端的
FIN
报文时,先回一个ACK
应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN
报文给客户端来表示同意现在关闭连接。
10. react新出来两个钩子函数是什么?和删掉的will系列有什么区别?
新增了下面两个生命周期方法:
getDerivedStateFromProps
getSnapshotBeforeUpdate
getDerivedStateFromProps 该方法类似于 componentWillReceiveProps
,可以用来控制 props
更新 state
的过程。它返回一个对象表示新的 state
。如果不需要更新组件,返回 null
即可。
用 getDerivedStateFromProps替换了 compoentWillMount
和compontWillReceiveProps
生命周期函数,解决还未渲染完成Dom
导致绑定的方法存在内存中,造成内存泄露
getSnapshotBeforeUpdate 方法在 React
对视图做出实际改动(如 DOM
更新)发生前被调用,返回值将作为 componentDidUpdate
的第三个参数。
用getSnapshotBeforeUpdate函数替换componetWillUpdate
方法,避免和CompoentDidUpdate
函数中获取数据不一致的问题
11. 最少说出三种前端清除浮动的方法?
方法一:额外标签法
给谁清除浮动,就在其后额外添加一个空白标签 ,给其设置clear:both
。
方法二:父元素添加overflow:hidden 通过触发BFC方式,实现清除浮动
方法三:使用after伪元素清除浮动
方法四:使用before和after双伪元素清除浮动
方法五:为父元素设置高度
12. 什么是强缓存和协商缓存?
强缓存是利用http
请求头中的Expires
和Cache-Control
两个字段来进行控制,用来表示资源的缓存时间;浏览器不会像服务器发送任何请求,而是直接从本地缓存中读取文件并返回Status Code
: 200 OK;
协商缓存是服务器用来确定缓存资源是否可用过期,向服务器发送请求,服务器会根据这个请求的request header
的一些参数来判断是否命中协商缓存,如果命中,则返回304状态码并带上新的response header
通知浏览器从缓存中读取资源
13. 说说React jsx转换成真实DOM的过程?
使用React.createElement
或JSX
编写React
组件,实际上所有的 JSX
代码最后都会转换成React.createElement(...)
,Babel
帮助我们完成了这个转换的过程。
createElement
函数对key
和ref
等特殊的props
进行处理,并获取defaultProps
对默认props
进行赋值,并且对传入的孩子节点进行处理,最终构造成一个虚拟DOM对象
ReactDOM.render
将生成好的虚拟DOM渲染到指定容器上,其中采用了批处理、事务等机制并且对特定浏览器进行了性能优化,最终转换为真实DOM
14. 说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?
react-redux:
react
官方推出的redux
绑定库,react-redux
将所有组件分为两大类:UI组件和容器组件,其中所有容器组件包裹着UI组件,构成父子关系。容器组件负责和redux
交互,里面使用redux API
函数,UI组件负责页面渲染,不使用任何redux API
。容器组件会给UI组件传递redux中保存对的状态和操作状态的方法
reduxjs/toolkit:
Redux
官方强烈推荐,开箱即用的一个高效的 Redux
开发工具集。它旨在成为标准的 Redux
逻辑开发模式,使用 Redux Toolkit
都可以优化你的代码,使其更可维护
15. React render方法的原理,在什么时候会触发?
原理:
在类组件中render
函数指的就是render
方法;而在函数组件中,指的就是整个函数组件
render
函数中的jsx
语句会被编译成我们熟悉的js代码,在render
过程中,react
将新调用的render
函数返回的树与旧版本的树进行比较,这一步是决定如何更新 DOM 的必要步骤,然后进行 diff
比较,更新dom树
触发时机:
类组件调用 setState
修改状态
函数组件通过useState hook
修改状态
一旦执行了setState
就会执行render
方法,useState
会判断当前值有无发生改变确定是否执行render
方法,一旦父组件发生渲染,子组件也会渲染
16. React性能优化的手段有哪些?
1.避免使用内联函数
2.使用react fragement
避免额外标记
3.使用immutable
,减少渲染的次数,为了避免重复渲染,会在shouldComponentUpdate()
中做对比,当返回true
,执行render
方法。immutable
通过is方法完成对比
4.懒加载组件
5.事件绑定方式(在constructor
中使用bind
绑定性能更高)
6.服务端渲染
7.组件拆分,合理使用hooks
17. React组件之间如何通信?
-
父组件向子组件通讯:
父组件可以向子组件传入props
的方式,向子组件进行通讯。 -
子组件向父组件通讯:
props
+回调的方式,父组件向子组件传递props
进行通讯,此props
为作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到⽗组件的作⽤域中。 -
兄弟组件通信:
兄弟组件之间的传递,则父组件作为中间层来实现数据的互通,通过使用父组件传递
例:组件A – 传值 --> 父组件 – 传值 --> 组件B -
跨层级通讯:
Context
设计⽬的是为了共享那些对于⼀个
组件树⽽⾔是“全局”的数据,
使用context
提供了组件之间通讯的一种方式,可以共享数据,其他数据都能读取对应的数据
例如当前认证的⽤户、主题或⾸选语⾔,对于跨越多层的全局数据通过Context
通信再适合不过。 -
发布订阅者模式:
发布者发布事件,订阅者监听事件并做出反应,我们可以通过引⼊event
模块进⾏通信。 -
全局状态管理工具:
借助Redux
或者Mobx
等全局状态管理⼯具进⾏通信,这种⼯具会维护⼀个全局状态中⼼Store
,并根据不同的事件产⽣新的状态。
18. 说说你对git rebase 和git merge的理解?区别?
在使用 git
进行版本管理的项目中,当完成一个特性的开发并将其合并到 master
分支时,会有两种方式:
git merge
git rebase
git rebase
与 git merge
都有相同的作用,都是将一个分支的提交合并到另一分支上,但是在原理上却不相同
git merge
将当前分支合并到指定分支,命令用法如下:
git merge xxx
git rebase
将当前分支移植到指定分支或指定commit
之上,用法如下:
git rebase -i <commit>
常见的参数有–continue,用于解决冲突之后,继续执行rebase
git rebase --continue
区别
merge
通过merge
合并分支会新增一个merge commit
,然后将两个分支的历史联系起来
其实是一种非破坏性的操作,对现有分支不会以任何方式被更改,但是会导致历史记录相对复杂
rebase
rebase
会将整个分支移动到另一个分支上,有效地整合了所有分支上的提交
主要的好处是历史记录更加清晰,是在原有提交的基础上将差异内容反映进去,消除了 git merge
所需的不必要的合并提交
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
能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。