【前端性能优化详细讲解优化】

浏览器主要组成与浏览器线程

界面控件 – 包括地址栏,前进后退,书签菜单等窗口上除了网页显示区域以外的部分

浏览器引擎 – 查询与操作渲染引擎的接口

渲染引擎 – 负责显示请求的内容。比如请求到HTML, 它会负责解析HTML、CSS并将结果显示到窗口中

网络 – 用于网络请求, 如HTTP请求。它包括平台无关的接口和各平台独立的实现

UI后端 – 绘制基础元件,如组合框与窗口。它提供平台无关的接口,内部使用操作系统的相应实现

JS解释器 - 用于解析执行JavaScript代码

数据存储持久层 - 浏览器需要把所有数据存到硬盘上,如cookies。新的HTML5规范规定了一个完整(虽然轻量级)的浏览器中的数据库web database

从输入 URL 到页面加载完成,发生了什么?

首先我们需要通过 DNS(域名解析系统,domain name system,域名系统)将 URL 解析为对应的 IP 地址,然后与这个 IP 地址确定的那台服务器建立起 TCP 网络连接,随后我们向服务端抛出我们的 HTTP 请求,服务端处理完我们的请求之后,把目标数据放在 HTTP 响应里返回给客户端,拿到响应数据的浏览器就可以开始走一个渲染的流程。渲染完毕,页面便呈现给了用户,并时刻等待响应用户的操作(如上图所示)

我们将这个过程切分为如下的过程片段:

DNS 解析

TCP 连接

HTTP 请求抛出

服务端处理请求,HTTP 响应返回

浏览器拿到响应数据,解析响应内容,把解析的结果展示给用户

大家谨记,我们任何一个用户端的产品,都需要把这 5 个过程滴水不漏地考虑到自己的性能优化方案内、反复权衡,从而打磨出用户满意的速度。

具体来说DNS(domain name system,域名系统,浏览器请求第三方服务器资源时,必须要将该域名解析为ip地址,浏览器才能发出请求,这个过程叫DNS。DNS实现了域名到ip的映射):

解析花时间,能不能尽量减少解析次数或者把解析前置?能——浏览器 DNS 缓存和 DNS prefetch。TCP 每次的三次握手都急死人,有没有解决方案?有——长连接、预连接、接入 SPDY 协议。如果说这两个过程的优化往往需要我们和团队的服务端工程师协作完成,前端单方面可以做的努力有限,那么 HTTP 请求呢?——在减少请求次数和减小请求体积方面,我们应该是专家!再者,服务器越远,一次请求就越慢,那部署时就把静态资源放在离我们更近的 CDN 上是不是就能更快一些?

以上提到的都是网络层面的性能优化。再往下走就是浏览器端的性能优化——这部分涉及资源加载优化、服务端渲染、浏览器缓存机制的利用、DOM 树的构建、网页排版和渲染过程、回流与重绘的考量、DOM 操作的合理规避等等——这正是前端工程师可以真正一展拳脚的地方。学习这些知识,不仅可以帮助我们从根本上提升页面性能,更能够大大加深个人对浏览器底层原理、运行机制的理解,一举两得!

什么是dns-prefetch:

DNS预解析,尝试在请求资源之前解析域名。可能是后面要加载的文件,也可能是用户将要打开的链接

dns-prefetch可以帮助开发人员掩盖DNS解析延迟

HTML属性,容错性非常好。不支持的浏览器遇到dns-prefetch提示,网站不会被中断

如何设置dns-prefetch:

如果需要浏览器对特定的域名进行解析,可以在页面中添加link标签实现,例如

<link rel="dns-prefetch" href="//g.alicdn.com" />
<link rel="dns-prefetch" href="//img.alicdn.com" />

注意事项:

  1、dns-prefetch仅仅对跨域域上的DNS查找有效,因此避免使用dns-prefetch指向自己的站点或域。因为当浏览器执行到这行时,你设置的站点或域背后的ip已经被解析了,写了也是白写

  2、如果该站点是通过https服务的,则此过程包括DNS解析,建立TCP连接以及执行TLS握手。可以将dns-prefetch与preconnect一起使用,dns-prefetch仅执行DNS预解析,preconnect会建立与服务器的连接,将二者结合起来可以进一步减少跨域请求的延迟

    <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
    <link rel="dns-prefetch" href="https://fonts.gstatic.com/">

  tip:如果页面需要建立许多与第三方的连接,将它们预先连接会适得其反。preconnect最好仅使用在最关键的那个连接上,其他的连接只需使用dns-prefetch即可节省DNS查找的时间

  3、dns-prefetch有个默认加载条件,网页中使用a标签href属性带的域名,不需要在head标签中加link手动设置的。但是a标签的默认启动在https不起作用,这时需要使用meta里面http-equiv来强制启动功能

  <meta http-equiv="x-dns-prefetch-control" content="on">

典型案例:

  1、淘宝

  2、京东

TCP的三次握手


三次握手建立连接:

  • 第一次握手:客户端发送syn(同步序列编号(Synchronize Sequence Numbers)是TCP/IP建立连接时使用的握手信号)包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;

  • 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;

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

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

经典的三次握手示意图:

经典的四次握手关闭图:

TCP长连接


所谓长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持(不发生RST包和四次挥手)。

连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接(一个TCP连接通道多个读写通信)。

这就要求长连接在没有数据通信时,定时发送数据包(心跳),以维持连接状态。

TCP短连接


短连接是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接(管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段)。

连接→数据传输→关闭连接。

SPDY 协议


SPDY协议是Google提出的基于传输控制协议(TCP)的应用层协议,通过压缩、多路复用和优先级来缩短加载时间。该协议是一种更加快速的内容传输协议。

SPDY协议的目标是优化HTTP协议的性能,通过压缩、多路复用和优先级等技术,缩短网页的加载时间并提高安全性。SPDY协议核心思想是尽量减少TCP连接数,而对于HTTP的语义未做太大修改(比如,HTTP的GET和POST消息格式保持不变),基本上兼容HTTP协议。

    

我们整个的知识图谱,用思维导图展示如下:

从原理到实践:各个击破

  1. 关键渲染路径与阻塞渲染

例如,当 HTML 解析器(HTML Parser)被脚本阻塞时,解析器虽然会停止构建 DOM,但仍会识别该脚本后面的资源,并进行预加载。

同时,由于下面两点:

1.1、CSS 被视为渲染 阻塞资源 (包括JS) ,这意味着浏览器将不会渲染任何已处理的内容,直至 CSSOM 构建完毕,才会进行下一阶段。

1.2、JavaScript 被认为是解释器阻塞资源,HTML解析会被JS阻塞,它不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性。

渲染树(Render-Tree)的关键渲染路径中,要求同时具有 DOM 和 CSSOM,之后才会构建渲染树。

即,HTML 和 CSS 都是阻塞渲染的资源。HTML 显然是必需的,因为包括我们希望显示的文本在内的内容,都在 DOM 中存放,那么可以从 CSS 上想办法。

最容易想到的当然是精简 CSS 并尽快提供它。除此之外,还可以用媒体类型(media type)和媒体查询(media query)来解除对渲染的阻塞。
<link href="index.css" rel="stylesheet">
<link href="print.css" rel="stylesheet" media="print">
<link href="other.css" rel="stylesheet" media="(min-width: 30em) and (orientation: landscape)">

第一个资源会加载并阻塞。第二个资源设置了媒体类型,会加载但不会阻塞,print 声明只在打印网页时使用。第三个资源提供了媒体查询,会在符合条件时阻塞渲染。

css放在头部,js放在body尾

2. 改变脚本加载次序defer与async

defer 与 async 可以改变之前的那些阻塞情形,这两个属性都会使 script 异步加载,然而执行的时机是不一样的。注意 async 与 defer 属性对于 inline-script 都是无效的,所以下面这个示例中三个 script 标签的代码会从上到下依次执行。

<script async>console.log("1")</script>
<script defer>console.log("2")</script>
<script>console.log("3")</script>

蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。

defer

defer 与相比普通 script,有两点区别:载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后

async

猜你喜欢

转载自blog.csdn.net/weixin_51225684/article/details/128921561