前端要知道的性能优化

文章提纲:

  1. TCP

  2. UDP

  3. 套接字插座

  4. HTTP协议

  5. DNS解析

  6. HTTP请求发起和响应

  7. 页面渲染的过程

  8. 页面的性能优化

TCP连接

TCP:传输控制协议,传输控制协议,是一种面向连接的,可靠的,基于字节流的传输层通信协议。
说的这么专业,有啥用呢?
先来举个个子子吧
还记得小时候我们做的纸杯电话么?两个纸杯用一条绳子连到一起,两个各拿一个纸杯把线拉直,一个对着纸杯讲,一个用耳朵对着纸杯听。

这其实就是一种最简单的连接通信,两人通过一根线连接起来,声音从这边的纸杯发出通过线传输到另一个纸杯接收,扩展到现在家家户户都有的固定电话也是如此,它的通信也是建立在双方可接受并且信任的基础上进行,如:

  1. 甲拿起电话,拨通0775-6532122,开始呼叫乙

  2. 乙听到电话声响起,拿起电话,此时甲收到乙已经拿起电话的声音

  3. 双方开始讲话。

回到我们的TCP协议,其实它和上面所说的电话协议差不多,只不过电话的协议是服务于电话通信,而根据tcp是服务于网络通讯的一种协议,类似的,通讯双方建立一次TCP连接,也需要经过三个步骤(握手)。

  1. 客户端发送SYN包(SYN = j)的到服务器,并进入SYN_SEND状态,等待服务器确认。

  2. 服务器收到SYN包,必须确认客户的SYN(ACK = J + 1),同时自己也发送一个SYN包(SYN = k)时,即SYN + ACK包,此时服务器进入SYN_RECV状态。

  3. 客户端收到服务器的SYN + ACK包,向服务器发送确认包ACK(ACK = K + 1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

     

上面几个唧唧歪歪的英文看的有点懵逼,翻译一下吧:(
大家最好记一下这些状态码,在服务器连接数的性能优化中会经常用到)

SYN:synchronous建立联机
ACK:确认确认
SYN_SENT:请求连接  
SYN_RECV:服务端被动打开后,接收到了客户端的SYN并且发送了ACK时的状态。再进一步接收到客户端的ACK就进入ESTABLISHED状态。

值得注意的是:tcp在握手过程中并不携带数据,(就像你打电话给酒店订房时,在确认对方是酒店客服人员之前,你也不会马上把身份证号码报给他吧?) ,而是在三次握手完成之后,才会进行数据传送

至于它的应用场景,其实是根据它本身的特点而定的,比如对网络通讯质量有要求,需要保证数据准确性时,就需要用到TCP协议了,如HTTP,FTP等文件传输协议,或一些邮件传输协议(SMTP,POP等)

UDP协议

UDP协议并非本文需要重点着笔的内容,但是讲到TCP了,作为他的互补兄弟,在此掠过一笔

UDP  :用户数据报协议用户数据报协议
相比于TCP的面向连接需要反复确认的繁琐步骤,UDP是一中性格特立独行并且主观性超强的非面向连接的协议,使用udp协议经常通信并不需要建立连接,它只是负责把数据尽可能快的发送出去,简单粗暴,并且不可靠,而在接收端,UDP把每个消息断放入队列中,接收端程序从队列中读取数据。

有人会说,UDP协议这么不可靠,为啥还会造出来呢?
话说回来,天底下没有无用之人,只有你不懂用的人而已,虽然UDP不可靠,但是它的传输速度快,效率高,在一些对数据准确性要求不高的场景,UDP就变得很有用了,比如QQ语音,QQ视频。

套接字插座

为什么要说嵌套字?
那是因为就像前面说的,TCP或UDP都是一种协议,也就是计算机网络通信中在传输层的一种协议,简单地说,就是一种约定,就像合作双方的合同一样,然后合同是死的,只有履行合同才是实质性的行动,因此无论是TCP还是UDP要产生作用,都需要有实际的行为去执行才能体现协议的作用
那么,有什么办法让这些协议作用呢?
这就要说到插座了。

socket:也叫嵌套字,是一组实现TCP / UDP通信的接口API,也就是说无论TCP还是UDP,通过对scoket的编程,都可以实现TCP / UCP通信,作为一个通信链的句柄,它包含网络通信必备的5种信息:

  1. 连接使用的协议

  2. 本地主机的IP地址

  3. 本地进程的协议端口

  4. 远地主机的IP地址

  5. 远地进程的协议端口

可见,插座包含了通信本方和对方的IP和端口以及连接使用的协议(TCP / UDP)通信双方中的一方(暂称:客户端)。通过SCOKET(嵌套字)对另一方(暂称:服务端)发起连接请求,服务端在网络上监听请求,当收到客户端发来的请求之后,根据插座里携带的信息,定位到客户端,就相应请求,把插座描述发给客户端,双方确认之后连接就建立了。
因此套接字之间的连接过程有三个步骤:

  1. 服务器监听:服务器实时监控网络状态等待客户端发来的连接请求

  2. 客户端请求:客户端根据远程主机服务器的IP地址和协议端口向其发起连接请求

  3. 连接确认:服务端收到套接字的连接请求之后,就响应请求,把服务端套接字描述发给客户端,客户端收到后一旦确认,则双方建立连接,进行数据交互。

通常情况下套接字连接就是TCP连接,因此插座连接一旦建立,通讯双方开始互发数据进行通信,直到其中一方或双方断开连接为止。

插座在即时通讯(QQ等各种聊天软件)等应用上应用广泛。

HTTP协议

HTTP协议:超文本传输​​协议也叫超文本传送协议,它是一种基于TCP / IP协议栈,在表示层和应用层上的协议(TCP在传输层的协议),通俗一点说就是:

  • TCP / IP是位于传输层上的一种协议,用于在网络中传输数据;

  • HTTP协议是应用层协议,基于TCP协议,用于包装数据,程序使用它进行通信,可以简单高效的处理通信中数据的传输和识别处理

而在现在应用非常广泛的HTTP连接则是建立在HTTP协议上的,处于应用层中的一种具体应用。
上面说到插座连接一旦建立就保持连接状态,而HTTP连接则不一样,它基于TCP协议的短连接,也就是客户端发起请求,服务器响应请求之后,连接就会自动断开,不会一直保持。

网址

前面讲了TCP,UDP,HTTP ...等等都是为了讲一个具体问题而做的知识点铺垫,那就是:。我们开发的网络应用中请求的发起和响应,一个的英文怎样的底层原理
我们都知道,网络应用绝大部分都是通过HTTP来进行请求的,而URL则是HTTP用来做连接建立和传输数据的一种具体实现,因此在此要简单讲一下URL。

URL:统一资源定位符统一资源定位符。说白了就是网络上用来标识具体资源的一个地址,包含了用户查找该资源的信息,HTTP使用它来传输数据和建立连接
一个URL有以下组成部分:

  1. 协议

  2. 服务器地址(域名或IP +端口)

  3. 路径

  4. 文件名

比如:HTTPS://www.baidu.com/index.html
其中

  1. https://是一种协议当然,HTTP也是ftp也是......

  2. www.baidu.com是服务器地址,当然你知道百度的IP也可以,例如我用ping命令得到百度的ip
    14.215.177.39,那么我可以用http://14.215.177.39打开百度

  3. index.html的包含了路径和文件名,当然通常的index.html是可以省略的,所以你打开百度时,并没有看到这个。

DNS

DNS:域名服务器,域名服务器。
是进行域名(域名)和与之相对应的IP地址(IP地址)转换的服务器.DNS中保存了一张域名(域名)和与之相对应的IP地址(IP地址)的表,以解析消息的域名。
在平时我们进行开发时,后端提供的接口地址通常是有IP地址加上端口号(8080什么鬼的)组成的,但是当我们把网站发布出去时,通常都需要把IP改成用域名。
为什么呢?
你想想哦,比如谷歌的地址是89.12.21.221:9090,百度的地址是132.21.33.221:8766 ......
这么一看你根本没有欲望是记住这些乱七八糟的数字吧?
但是域名就不一样了,比如谷歌的google.com,百度的baidu.com是不是一遍就记住了呢?
所以为了处理这个问题,就需要用域名去映射IP地址,达到易记易用的目的。

因此,当用户在浏览器输入https://www.baidu.com回车时,它经历了以下步骤:

  1. 浏览器根据地址去本身缓存中查找DNS解析记录,如果有,则直接返回IP地址,否则浏览器会查找操作系统中(主机文件)是否有该域名的DNS解析记录,如果有则返回。

  2. 如果浏览器缓存和操作系统的主机中均无该域名的DNS解析记录,或者已经过期,此时就会向域名服务器发起请求来解析这个域名。

  3. 请求会先到LDNS(本地域名服务器),让它来尝试解析这个域名,如果LDNS也解析不了,则直接到根域名解析器请求解析

  4. 根域名服务器给LDNS返回一个所查询余的主域名服务器(gTLDServer)地址。

  5. 此时LDNS再向上一步返回的通用顶级域名服务器发起解析请求。

  6. gTLD服务器接收到解析请求后查找并返回此域名对应的名称服务器域名服务器的地址,这个名称服务器通常就是你注册的域名服务器(比如阿里dns,腾讯dns等)

  7. 名称服务器域名服务器会查询存储的域名和IP的映射关系表,正常情况下都根据域名得到目标IP记录,连同一个TTL值返回给DNS服务器域名服务器

  8. 返回该域名对应的IP和TTL值,本地DNS服务器会缓存这个域名和IP的对应关系,缓存的时间有TTL值控制。

  9. 把解析的结果返回给用户,用户根据TTL值缓存在本地系统缓存中,域名解析过程结束。

HTTP请求发起和响应

如果这篇文章的主题是网络通信,那到这里已经可以告一段落了,但今天我们要讲的是网络应用中请求的发起和响应以及页面渲染的原理,因此以上只是铺垫。
在一个网络程序开发中,一般都有前端和后端之分,前端负责向后端请求数据和展示页面,后端负责接收请求和做出响应发回给前端,他们之间的协作的桥梁是什么呢
是API
API是什么?不就是一个URL吗?
URL又是啥呢?上面说到就是HTTP连接的一种具体的载体
因此,
无论对于前端或者是后端,理解HTTP,无论是对自身对编程的理解,还是和同事协作,都是好处大大的,
下面,根据上面各个知识点的理解,我们来整理一下并解决一下上面提到的第一个问题
从用户输入URL,到浏览器呈现给用户页面,经过了什么过程

  1. 用户输入网址,浏览器获取到URL

  2. 浏览器(应用层)进行DNS解析(如果输入的是IP地址,此步骤省略)

  3. 根据解析出的IP地址+端口,浏览器(应用层)发起HTTP请求,请求中携带(请求头报头(也可细分为请求行和请求头),请求体体),

报头包含:

  1. 请求的方法(GET,POST,PUT ..)

  2. 协议(HTTP,HTTPS,FTP,SFTP ...)

  3. 目标URL(具体的请求路径已经文件名)

  4. 一些必要信息(缓存,饼干之类)

体包含:

  1. 请求的内容

  1. 请求到达传输层,TCP协议为传输报文提供可靠的字节流传输服务,它通过三次握手等手段来保证传输过程中的安全可靠。通过对大块数据的分割成一个个报文段的方式提供给大量数据的便携传输。

  2. 到网络层,网络层通过ARP寻址得到接收方的Mac地址,IP协议把在传输层被分割成一个个数据包传送接收方。

  3. 数据到达数据链路层,请求阶段完成

  4. 接收方在数据链路层收到数据包之后,层层传递到应用层,接收方应用程序就获得到请求报文。

  5. 接收方收到发送方的HTTP请求之后,进行请求文件资源(如HTML页面)的寻找并响应报文

  6. 发送方收到响应报文后,如果报文中的状态码表示请求成功,则接受返回的资源(如HTML文件),进行页面渲染。

页面的渲染

当一个请求的发起和响应都完成之后,浏览器就会收到响应内容,但浏览器收到的是一串串的代码或网址链接,怎么把这些代码转化成用户可以看得懂的界面呈现出来,就是浏览器的工作了。
目前市场上的浏览器已经不下百种,各个浏览器根据内核又可以分成几大类,每一类浏览器对页面的渲染原理和过程有所差异

但总的来说,各个浏览器渲染页面都基本遵循如下图的流程:

 

图中有几处英文词汇可能不好理解,没关系,先做一下解释:

  1. HTML解析器:HTML解析器,其本质是将HTML文本解释成DOM树。

  2. CSS解析器:CSS解析器,其本质是讲DOM中各元素对象加入样式信息

  3. JavaScript的引擎:专门处理JavaScript的脚本的虚拟机,其本质是解析JS代码并且把逻辑(HTML和CSS的操作)应用到布局中,从而按程序要的要求呈现相应

  4. DOM树:文档对象模型树,也就是浏览器通过HTMLparser解析HTML页面生成的HTML树状结构以及相应的接口。

  5. 渲染树:渲染树,也就是浏览器引擎通过DOM树和CSS规则树构建出来的一个树状结构,和dom树不一样的是,它只有要最终呈现出来的内容,像或者带有显示:无的节点是不存在渲染树中的。

  6. 布局:也叫reflow重排,渲染中的一种行为。当rendertree中任一节点的几何尺寸发生改变了,渲染树都会重新布局。

  7. 重绘:重绘,渲染中的一种行为。渲染树中任一元素样式属性(几何尺寸没改变)发生改变了,渲染树都会重新画,比如字体颜色,背景等变化。

所以,根据关键词汇的解释以及顺着流程图的流程,可以总结出,浏览器解析渲染页面主要包括以下过程:

  1. 浏览器通过HTMLParser根据深度遍历的原则把HTML解析成DOM树。

  2. 将CSS解析成CSS规则树(CSSOM树)。

  3. 根据DOM树和CSSOM树来构造渲染树。

  4. layout:根据得到的render tree来计算所有节点在屏幕的位置。

  5. 油漆:遍历呈现树,并调用硬件图形API来绘制每个节点。

前端性能优化

对于页面渲染基本上这样就是一个的流程,看完之后,有没有什么感觉在实际编码中可以优化的点呢?没有吧?因为很多细节都没有讲述,因此为了找到可优化的点,在此对页面渲染过程的几个关键步骤做一下陈述:

1. HTML解析:

HTML解析器,HTML解析器,HTML解析器,HTML解析器脚本,链接,风格等标签会使解析过程产生阻塞,阻塞的情况有:

  1. 外部样式会阻塞内部脚本的执行。

  2. 外部样式与外部脚本并行加载,但外部样式会阻塞外部脚本执行。

  3. 如果外部脚本带有异步属性,则外部脚本的加载与执行不受外部样式影响

  4. 如果链接标签是动态创建(JS生成),不管有无异步属性,都不会阻塞外部脚本的加载与执行。

2. CSS解析:

CSS Parser作用就是将很多个CSS文件中的样式合并解析出具有树形结构样式规则,在对样式解析的过程中,默认CSS选择器是从右往左进行解析的。至于为什么是从右到左左,而不是从左到右,也是不会从左到左...
下面举个栗子来说一下:
假如现在有这样的一个样式:

1#parent .ch1 .dh1 {}
2.fh1 .ch1 .dh1{}
3.ah1 .ch1 .eh1 {}
4#parent .fh1 {}
5.ch1 .dh1{}

我们来比较从左到右和从右到左两种方式的结果:

 


从两个图的比较就可以看几点:

  1. 右边的树复杂度要比左边的低

  2. 右边的树公用样式重合度比左边的低

  3. 右边的树从根开始的节点数要比左边的少

可能光看这几点没看出什么问题,但你要知道:浏览器中的CSS解析器负责CSS的解析,并为每个节点计算出样式,因此虽然CSS解析器要做的事情不多,但要每个节点都要进行遍历查找计算,计算量极大,因此解析的方式是决定其性能的关键点
就如

1#parant .a{}
2和
3.a{}

估计绝大多数人都会认为前者要比后者性能更优,其实不然,在解析过程中 
#paran .a {}意味着css解析器要先找到#parent再找到他下面的.a所在节点
而后者可以直接定位到.A {}因此哪一种方式更优,显而易见。

3.脚本执行:

浏览器解析HTML时,当遇到<SCRIPT>标签就会立即解析脚本,同时阻塞解析文档直到脚本执行完毕(你可能问为什么要这样设计,明显啊,脚本的执行是改变的CSS和DOM中,会造成渲染树不停的重绘和重排的),而当<script>是引入外部js文件时,会阻塞到js文件下载完成并且执行完成为止(除非加了defer或者async属性)。脚本在解析过程中将对dom或css的操作解析出来加入到DOM Tree和cssom中。


性能优化

把这些度讲完之后,对于性能优化的点,相信大家心里都有点X数了吧,下面简单总结一下日常开发过程中常用的性能优化的地方:

1.对于CSS:

  1. 优化选择器路径:健全的css选择器固然是能让开发看起来更清晰,然后对于css的解析来说却是个很大的性能问题,因此相比于.a .b .c {},更倾向于大家写的.c {}。

  2. 压缩文件:尽可能的压缩你的CSS文件大小,减少资源下载的负担。

  3. 选择器合并:把有共同的属性内容的一系列选择器组合到一起,能压缩空间和资源开销

  4. 精准样式:尽可能减少不必要的属性设置,比如你只要设置{padding-left:10px}的值,那就避免{padding:0 0 0 10px}这样的写法

  5. 雪碧图:在合理的地方把一些小的图标合并到一张图中,这样所有的图片只需要一次请求,然后通过定位的方式获取相应的图标,这样能避免一个图标一次请求的资源浪费。

  6. 避免通配符:.a .b * {}像这样的选择器,根据从右到左的解析顺序在解析过程中遇到通配符(*)回去遍历整个dom的,这样性能问题就大大的了。

  7. 少用浮子:浮在渲染时计算量比较大,尽量减少使用。

  8. 0值去单位:对于为0的值,尽量不要加单位,增加兼容性

2.对于JavaScript的:

  1. 尽可能把脚本标签放到身体之后,避免页面需要等待JS执行完成之后DOM才能继续执行,最大程度保证页面尽快的展示出来。

  2. 尽可能合并脚本代码,

  3. CSS能干的事情,尽量不要用的JavaScript来干。毕竟的JavaScript的解析执行过于直接和粗暴,而CSS效率更高。

  4. 尽可能压缩的JS文件,减少资源下载的负担

  5. 尽可能避免在JS中逐条操作DOM样式,尽可能预定义好CSS样式,然后通过改变样式名来修改DOM样式,这样集中式的操作能减少回流或重绘的次数。

  6. 尽可能少的在JS中创建DOM,而是预先埋到HTML中用显示:无来隐藏,在JS中按需调用,减少JS对DOM的暴力操作。

3.对于HTML:

  1. 避免再HTML中直接写CSS代码。

  2. 使用视口加速页面的渲染。

  3. 使用语义化标签,减少CSS的代码,增加可读性和搜索引擎优化。

  4. 减少标签的使用,DOM解析是一个大量遍历的过程,减少无必要的标签,能降低遍历的次数。

  5. 避免SRC,HREF等的值为空。

  6. 减少DNS查询的次数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值