金三银四的季节又来了,笔者一直在武汉工作,年前裸辞后,过完年一共面了接近两个星期,有阿里,腾讯,金山,万科,趣头条等公司,在武汉拿到了4个offer(金山、万科、航班管家、财人汇)后,又在深圳一站式面了腾讯,最终选择了腾讯,这里不完全记录一点面试中遇到的问题
float和flex的区别 为什么会有flex 了解CI CD吗 React和Vue的差异 移动端高清问题 移动端点击穿透问题 Nodejs的模块化和ES6的模块化区别 http的优缺点 restful和graphQL优缺点,对比 事件委托的优缺点 何时需要用到border-box csrf是什么,如何用预防 fastclik的作用 前端性能优化 CSS居中+等比宽高问题 margin-top/margin-bottom百分数问题 TCP和UDP区别
float 脱离文档流 布局的传统解决方案,基于盒状模型,依赖 display 属性 + position属性 + float属性。它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。 缺点:
- 容错性差,耦合度高,其中一个浮动元素出现问题,其他也会受影响
- 必须要有固定的布局
- 低版本IE不兼容
flex W3C 提出了一种新的方案----Flex 布局,可以简便、完整、响应式地实现各种页面布局 flex性能改进在于,
- 相比传统margin,可伸缩性强(不算性能)
- 相比float,关联性重绘好很多
- 相比tranform,子元素和父元素样式控制操作更少
RESTful优缺点 特点:充分利用 HTTP 协议本身语义。 无状态,这点非常重要。在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了复杂度。 HTTP 本身提供了丰富的内容协商手段,无论是缓存,还是资源修改的乐观并发控制,都可以以业务无关的中间件来实现。
相似处 Virtual DOM、组件化、Props 差异
- 渲染优化,是否需要用shouldComponentUpdate,PureComponent
- JSX 和 Template , CSS in JS
- Vue-cli 和 create-react-app
- 学习难度,学 React 前,你需要知道 JSX 和 ES2015
- DPR (device pixel ratio)= device pixel(设备像素) \ CSS pixel(css像素)
- 1px解决方案
- border-image 、 background-image 缺点:修改颜色麻烦,圆角需要特殊处理
- box-shadow 缺点:在Safari下不支持小数设置
- 伪类 + transform
- SVG
- viewport + rem
场景:上层元素A绑定了tap事件,下层元素B绑定了click事件。当我们点击A时,A元素隐藏,此时也会触发下层B元素的click事件。 原因就是当上层A的tap事件发生后,其实是touchend结束后,会触发click事件,因为这几个事件的触发顺序是touchstart-touchmove-touchend-click。因此当click事件触发300ms后,上面的A元素已经消失,此时真正点击的就相当于下层的B元素,因此会发生点击穿透事件。 解决方案
- 都使用click或者tap
- 在click事件触发前阻止,在touchend事件中使用e.preventDefault()
- 使用fast-click。fastclick是一个专门解决300ms点击延迟的库。
FastClick实现原理
在document.body绑定touchstart和touchend事件
touchstart
用于记录当前点击的元素的targetElement
touchend
- 阻止默认事件(屏蔽之后的click事件)
- 合成click事件,并添加可跟踪属性forwardedTouchEvent
- 在targetElement上触发click事件
- targetElement上绑定的事件立即执行,完成FastClick
CommonJS主要依赖module、exports、require、global这几个环境变量。
CommonJS用同步方式加载模块。
ES6 module的模块不是对象,import
命令会被JS引擎动态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。(ps: 也正是因为这个,使得静态分析成为可能)
差异
- CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用
- CommonJS模块输出值的拷贝,一旦输出一个值,模块内部的变化就影响不到这个值。
- ES6模块的运行机制与CommonJS不一样。JS引擎对脚本静态分析的时候,遇到模块加载命令
import
,就会生产一个只读引用。等到脚本真正执行时,再根据只读引用,到被加载的模块中取值。原始值变了,import
加载的值也会跟着变。因此ES6模块是动态引用,并且不会缓存值,模块里的变量绑定其所在的模块。
- CommonJS模块是运行时加载,ES6模块是编译时输出接口
- 运行时加载:CommonJS模块就是对象,即在输入时先加载整个模块,生产一个对象,然后再从这个对象上面读取方法,这种加载成为”运行时加载“
- 编译时加载:ES6模块不是对象,而是通过
export
命令显示指定输出的代码,import
时采用静态命令的形式。即在import
时可以指定加载某个输出值,而不是加载整个模块,这种加载成为编译时加载 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
优点:
- 轻量,直接基于http,不需要任何别的消息协议。get/post/put/delete为CURD操作
- 面向资源,一目了然,具有自解释性
- 数据描述简单,一般是xml,json做数据交换
缺点: 一个适用于简单操作的接口规范,复杂操作并不适用,适合CRUD并且只适合CRUD,有的浏览器可能不支持POST、GET以外的操作,要特殊处理。 ps: 多个接口,更新订单收款状态、更新订单支付状态、更新订单结算状态。批量操作,get超出长度
- 客户端-服务器:提供服务的服务器和使用服务的客户端分离解耦
- 提高客户端的便捷性(操作简单)
- 简化服务器提高可伸缩性(高性能、低成本)
- 允许客户端服务端分组优化不受影响
- 无状态:来自客户的每一个请求必须包含服务器处理该请求所需的所有信息(请求信息唯一性)
- 提高了可见性(可以考虑每个请求)
- 提高了可靠性(更容易故障恢复)
- 提高了可扩展性(降低了服务器资源使用)
- 可缓存:服务器必须让客户端知道请求是否可以被缓存?如果可以,客户端可以重用之前的请求信息发送请求
- 减少交互连接数
- 减少链接过程的网络延迟
- 分层系统:允许服务器和客户端之前的中间层(代理,网关等)代替服务器对客户端的请求进行回应,而客户端不需要关心与它交互的组件之外的事情
- 提高了系统的可扩展性
- 简化了系统的复杂性
- 统一接口:客户和服务器之前通信的方法必须是统一的。
提高交互的可见性 鼓励单独优化改善组件
- 支持按需代码:服务器可以提供一些代码或者脚本并在客户的运行环境中执行
- 提高了可扩展性
优点:
- 节省内存,减少事件注册。
- 适合动态内容
确定:
- 如果把所有事件都是用事件代理,可能会出现误判。即本不该被触发的事件被绑定上了事件。
- 基于冒泡,对于不冒泡的事件不支持。
- 层次过多时,可能会被某曾组织掉。
特点
- CSRF通常发生在第三方域名
- CSRF攻击者不能获取到Cookie等信息,只是使用。
防御策略
-
阻止不明外域的访问
- 同源监测
- Samesite cookie
-
提交时要求附加本域下才能获取的信息
- CSRF TOken
- 双重Cookie验证
- 提交验证码
浏览器渲染过程
解析DOM Tree,创建一个或多个渲染层(layer) 将每个层独立的绘进位图中(计算样式->布局->栅格化) 将层作为纹理(texture)上传至GPU 复合(composite)多个层来生成最终的屏幕图像 每个层的样式出现调整后,要重新计算样式->重新布局->重新栅格化->重新组合
使用top/left只会创建一个层,而是用transform会使浏览器将元素单独提取放在GPU单独的渲染层中,这样有三点好处:
- 该元素任何合成属性(Composite Property)的变化将不会影响原有文档,不会导致文档被重新布局(重绘、回流)
- 该层将由GPU负责渲染,从而节省CPU资源,不会阻塞主线程JS代码的执行
- 动画更为平顺,这是因为使用transform可以小于像素的单位进行绘制 可能带来的负作用是额外的渲染层导致更多的线程间通信,如果过度使用,导致生成成百上千的渲染层,那反而会导致组合各层图像的成本迅速上升成为主要矛盾,且我们需要记住GPU也是有内存限制的。
使用calc(100vw / 屏幕宽度)
[] == ![] 这个要牵涉到 JavaScript 中不同类型 == 比较的规则, 具体是由相关标准定义的. ![] 的值是 false, 此时表达式变为 [] == false, 参照标准, 该比较变成了 [] == ToNumber(false), 即 [] == 0. 这个时候又变成了 ToPrimitive([]) == 0, 即 '' == 0, 接下来就是比较 ToNumber('') == 0, 也就是 0 == 0, 最终结果为 true.
- font-size em-square
- line-height 实际占地高度
- 100px 100px -> 103px
- vertical top middle bottom text-top text-bottom
- 图片下面有空隙
- vertical-align top
- img{display: block}
- font-size 0 傻逼才用
- inline-block 元素对不齐 无解 用 flex或float
- inline-block 有空隙 用 flex 或 float
<main>
<span>Center me, please!</span>
</main>
- display: flex + margin: auto
main{
width: 100%;
min-height: 152px;
display: flex;
}
main > span {
margin: auto;
}
- display: flex , grid
main{
width: 100%;
min-height: 152px;
display: grid;
justify-content: center;
align-items: center;
}
main > span {
background: #b4a078;
color: white;
padding: .3em 1em .5em;
border-radius: 3px;
box-shadow: 0 0 .5em #b4a078;
}
- position: absolute + calc()
main{
width: 100%;
min-height: 152px;
display: flex;
}
main > span {
position: absolute;
top: calc(50% - 16px);
left: calc(50% - 72px);
}
- position: absolute + translate
main{
width: 100%;
min-height: 152px;
display: flex;
}
main > span {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
}
```
5. display: table / table-cell + vertical-align: middle
```css
main {
width: 100%;
height: 152px;
display: table;
}
main > div {
display: table-cell;
text-align: center;
vertical-align: middle;
}
- 伪元素 :after + vertical-align: middle
main {
width: 100%;
height: 152px;
text-align: center;
}
main::after {
content:'';
display: inline-block;
height: 100%;
vertical-align: middle;
}
- 使用隐藏图片 完美兼容PC端、移动端 如果div容器不给高度,它的高度会随容器内部元素的变化而撑大 在容器内添加一张等比宽高的图片,给图片设置宽度100% 高度auto
<style>
#container {
width: 100%;
}
.attr {
background-color: #008b57;
}
.attr img{
width: 100%;
height: auto;
}
</style>
<div id='container'>
<div class='attr'>
<img src="1.png" alt="">
</div>
</div>
还可以用base64优化
- 使用vmin 将父容器的宽度和高度定义为相同vmin,这样父容器宽高就是相同值,然后给子容器宽高设置百分比
- vw 相对于视窗的宽度
- vh 相对于视窗的高度
- vmin 相对于视口的宽度或高度中较小的那个被均分为100单位的vmin
- vmax 相对于视口的宽度或高度中较大的那个被均分为100单位的vmax
#container{
width: 100vmin;
height: 100vmin;
}
.attr {
width: 50%;
height: 50%;
background-color: orange;
}
- 使用scale
.attr{
width:50%;
height: calc(50%);
}
- padding-top/padding-bottom 实现 因为padding-bottom的属性值百分比是按照父容器的宽度来计算。
<style type="text/css">
#container {
width: 80%;
height: 500px;
}
.attr {
width: 50%;
height: 0;
padding-bottom: 50%;
background-color: #008b57;
}
</style>
<div id='container'>
<div class='attr'></div>
</div>
CSS权威指南中的解释:
我们认为,正常流中的大多数元素都会足够高以包含其后代元素(包括外边距),如果一个元素的上下外边距时父元素的height的百分数,就可能导致一个无限循环,父元素的height会增加,以适应后代元素上下外边距的增加,而相应的,上下外边距因为父元素height的增加也会增加,如果循环。
###UDP和TCP
简介:UDP是面向无连接的,UDP只是数据报文的搬运工,不保证有序且不丢失的传递,UDP协议没有控制流量算法,相比TCP更加轻量
UDP不需要和TCP一样在发送数据前进行三次握手建立连接。 并且只是数据报文的搬运工,不会对数据报文进行任何拆分和拼接。 具体来说:
- 在发送端,应用层的数据 -> 传输层UDP协议,UDP只会给数据增加一个UDP头标识
- 在接收端,网络层将数据传给传输层,UDP也只去除IP报文头,就传给应用层
首先不可靠性体现在无连接上,通信都不需要建立连接,想发就发,这样的情况肯定不可靠。 网络不佳的情况下,UDP因为没有拥塞控制,一直会以恒定的速度发送数据。弊端就是网络不好会丢包,优点就是在某些时效性要求高的场景适合。
UDP的头部开销小,只有八个字节(TCP至少二十字节) UDP头部包含以下几个数据:
- 两个十六位端口号,分别为源端口和目标端口
- 整个数据报文的长度
- 整个数据报文的检验
UDP支持一对一,一对多,多对多,多对一,也就是单播,多播,广播
- 直播
- 游戏
建立和断开连接都要进行握手。在传输过程中,通过各种算法保证数据的可靠性。 相对UDP来说不那么的高效。
- Sequence number,这个序号保证了TCP传输的报文是有序的,对端可以通过序号顺序拼接报文
- Acknowledgement number,这个序号表示数据接收端 期望接受的下一个字节的编号是多少,同时也表示上一个序号的数据已经收到
- Window Size,窗口大小,表示还能接受多少字节的数据,用于流量控制
- 标识符
- 建立连接三次握手 不管是客户端还是服务端,TCP 连接建立完后都能发送和接收数据,所以 TCP 是一个全双工的协议。
客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态。
服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序列号,发送完成后便进入SYN-RECEIVED状态。
当客户端收到连接同意的应答后,还要向服务端发送一个确认报文。 客户端发完这个报文段便进入ESTABLISHED状态。 服务端发完这个应答后也会进入ESTABLISHED状态。
ps: 为什么 TCP 建立连接需要三次握手,明明两次就可以建立起连接 因为这是为了防止出现失效的连接请求报文段被服务端接收的情况,从而产生错误。
- 第一次握手 若客户端 A 认为数据发送完成,则它需要向服务端 B 发送连接释放请求。
- 第二次握手 B 收到连接释放请求后,会告诉应用层要释放 TCP 链接。然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,此时表明 A 到 B 的连接已经释放,不再接收 A 发的数据了。但是因为 TCP 连接是双向的,所以 B 仍旧可以发送数据给 A。
- 第三次握手 B 如果此时还有没发完的数据会继续发送,完毕后会向 A 发送连接释放请求,然后 B 便进入 LAST-ACK 状态。
- 第四次握手 A 收到释放请求后,向 B 发送确认应答,此时 A 进入 TIME-WAIT 状态。该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,若该时间段内没有 B 的重发请求的话,就进入 CLOSED 状态。当 B 收到确认应答后,也便进入 CLOSED 状态。 ps: 2MSL 为了保证 B 能收到 A 的确认应答。若 A 发完确认应答后直接进入 CLOSED 状态,如果确认应答因为网络问题一直没有到达,那么会造成 B 不能正常关闭。
ARQ协议就是超时重传机制。通过确认和超时机制保证数据的正确送达。 ARQ协议包含停止等待ARQ和连续ARQ两种协议。