作者:韩小窝
https://mp.weixin.qq.com/s/wJxj5QbOHwH9cKmqU5eSQw

2022/05/20 【Web页面全链路性能优化指南】 - 图1
性能优化不单指优化一个页面的打开速度,在开发环境将一个项目的启动时间缩短使开发体验更好也属于性能优化,大文件上传时为其添加分片上传、断点续传也属于性能优化。在项目开发以及用户使用的过程中,能够让任何一个链路快一点,都可以被叫做性能优化。

浏览器渲染原理

我们需要知道浏览器是如何渲染一个页面的,我们才能知道如何对页面进行性能优化,所以这里我们对一些基础知识进行讲解

进程与线程

浏览器有多种进程,其中最主要的5种进程如下2022/05/20 【Web页面全链路性能优化指南】 - 图2

  1. 浏览器进程 负责界面展示、用户交互、子进程管理、提供存储等
  2. 渲染进程 每个页面都有一个单独的渲染进程,用于渲染页面,包含webworker线程
  3. 网络进程 主要处理网络资源加载(HTML、CSS、JS、IMAGE、AJAX等)
  4. GPU进程 3D绘制,提高性能
  5. 插件进程 chrome插件,每个插件占用一个进程

输入url到页面展示完整过程

图1
2022/05/20 【Web页面全链路性能优化指南】 - 图3
图1

1.用户输入

用户在浏览器进程输入并按下回车健后,浏览器判断用户输入的url是否为正确的url,如果不是,则使用默认的搜索引擎将该关键字拼接成url。

2.卸载原页面并重定向到新页面

然后浏览器会将现有页面卸载掉并重定向到用户新输入的url页面,也就是图中【Process Unload Event】和【Redirect】流程。
此时浏览器会准备一个渲染进程用于渲染即将到来的页面,和一个网络进程用于发送网络请求。

3.处理Service Worker

如果当前页面注册了Service Worker那么它可以拦截当前网站所有的请求,进行判断是否需要向远程发送网络请求。也就是图中【Service Worker Init】与【Service Worker Fecth Event 】步骤
如果不需要发送网络请求,则取本地文件。如果需要则进行下一步。

4.网络请求

OSI网络七层模型:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层 在实际应用中物理层、数据链路层被统称为物理层,会话层、表示层、应用层被统称为应用层,所以实际使用时通常分为4个层级

【物理层】>【网络层(IP)】>【传输层(TCP/UDP)】>【应用层(HTTP)】
也就是图中【HTTP Cache】、【DNS】、【TCP】、【Request】、【Response】步骤
图2
2022/05/20 【Web页面全链路性能优化指南】 - 图4
图2
浏览器会拿着url通过网络进程进行如下步骤

  1. 根据url查询本地是否已经有强制缓存,如果有则判断缓存是否过期,如果没过期则直接返回缓存内容,也就是图1中【HTTP Cache】步骤
  2. 如果没有强制缓存或者缓存已过期,则将该请求加入队列进行排队准备发送网络请求,也就是图2中【正在排队】,然后进入DNS解析阶段,也就是图1中【DNS】以及图2中的【DNS查找】,DNS根据域名解析出对应的IP地址。(DNS基于UDP)。
  3. 然后使用IP寻址找到对方,然后根据IP地址+端口号创建一个TCP连接(三次握手),也就是图1中【TCP】以及图2中的【初始连接】创建完成后利用TCP连接来传输数据。(TCP会将数据拆分为多个数据包,进行有序传输,如果丢包会重发,TCP的特点是可靠、有序)
  4. 判断当前协议是否为https,如果为https,则进行SSL协商,将数据进行加密,如果为http协议则不进行加密(明文传输),也就是图2中的【SSL】。
  5. 开始发送http请求(请求行/请求头/请求体),也就是图1中【Request】以及图2中的【已发送请求】。HTTP协议有多个版本,目前使用最多的版本为HTTP/1.1,HTTP/1.1发送完成后默认不会断开。keep-alive 默认打开,为了下次传输数据时复用上次创建的连接。每个域名最多同时建立6个TCP连接,所以同一时间最多发生6个请求。HTTP协议的各个版本特性如下:
    • HTTP/0.9 没有请求头和响应头,不区分传输的内容类型,因为当时只传输HTML。
    • HTTP/1.0 提供了请求头和响应头,可以传输不同类型的内容数据。根据请求响应头的不同来处理不同的资源,HTTP1.0每次发完请求都会断开TCP连接。有新的请求时再次创建TCP连接。
    • HTTP/1.1 默认开启了 keep-alive ,它能够让一个TCP连接中传输多个HTTP请求,也叫链路复用。但一个TCP连接同一时间只能发送一个HTTP请求,为了不阻塞多个请求,Chrome允许创建6个TCP连接,所以在HTTP/1.1中,最多能够同时发送6个网络请求。
    • HTTP/2.0 HTTP/2.0使用同一个TCP连接来发送数据,他把多个请求通过二进制分贞层实现了分贞,然后把数据传输给服务器。也叫多路复用,多个请求复用同一个TCP连接。HTTP/2.0会将所有以:开头的请求头做一个映射表,然后使用hpack进行压缩,使用这种方式会使请求头更小。服务器可主动推送数据给客户端。
    • HTTP/3.0 使用UDP实现,在UDP上一层加入一层QUIC协议,解决了TCP协议中的队头阻塞问题。
  6. 服务器收到数据后解析HTTP请求(请求行/请求头/请求体),处理完成后生成状态码和HTTP响应(响应行/响应头/响应体)后返回给客户端,也就是图2的【等待中】在做的事情。
  7. 客户端接收到HTTP响应后根据状态码进行对应的处理,如果状态码为304则直接代表协商缓存生效,直接取本地的缓存文件。如果不是则下载内容。也就是图1中【Response】以及图2中的【下载内容】步骤。

    5.服务端响应

    在4.网络请求第6步中,服务器收到HTTP请求后需要根据请求信息来进行解析,并返回给客户端想要的数据,这也就服务端响应。
    服务端可以响应并返回给客户端很多种类型的资源,这里主要介绍html类型
    目前前端处理服务端响应html请求主要分为SSR服务端渲染与CSR客户端渲染,CSR就是返回一个空的HTML模版,然后浏览器加载js后通过js动态渲染页面。SSR是服务端在接受到请求时事先在服务端渲染好html返回给客户端后,客户端再进行客户端激活。
    在打开一个站点的首屏页的完整链路中,使用SSR服务端渲染时的速度要远大于CSR客户端渲染,并且SSR对SEO友好。所以对于首屏加载速度比较敏感或者需要优化SEO的站点来说,使用SSR是更好的选择。

6.浏览器渲染详细流程

浏览器渲染详细流程主要在4.网络请求中的地7步。浏览器下载完html内容后进行解析何渲染页面的流程。
2022/05/20 【Web页面全链路性能优化指南】 - 图5
渲染流程分为4种情况,

  1. HTML中无任何CSS相关标签
  2. CSS相关标签在HTML最顶部,且在解析到内容标签(
    )时已经解析完CSS相关标签
  3. CSS相关标签在HTML最顶部,但在解析到内容标签(
    )时CSS相关标签尚未解析完
  4. CSS相关标签在HTML最底部

下面的流程是对上图的文字版解析。读者可将以上4种情况分别带入到如下的渲染流程中走一遍。就能理解浏览器的完整渲染过程了。

【HTML】

浏览器收到html资源后先预扫描并加载对应资源

【HTML Parser】

对HTML字符串从上到下逐行解析,每解析完成一部分都会拿着解析结果进入下一步骤

【DOM Tree】

css相关标签跳过此步骤
如果当前解析结果为

相关标签,则生成DOM树(window.document)后进入下一步。
如果当前解析结果为相关标签且并且没有添加异步属性,则先停止【HTML Parser】的进行,等待资源加载完成后,然后按照以下2种情况处理,当处理完成后便停止当前标签后续步骤的执行,并继续进行新标签【HTML Parser】步骤的解析

  1. 如果HTML从未解析到过css相关节点则立即执行。(此时页面会把之前的内容都显示在页面上)
  2. 如果HTML已经解析到过css相关节点则等待css相关节点解析完成后再执行。(在CSS解析完的一瞬间会触发之前所有等待CSS资源解析的任务,假如在解析之前还有
    的话,理论上
    应该在执行之前被绘制到页面上,但因为Chrome是按照贞为单位来进行元素的绘制的,如果绘制
    与执行的时间在一贞之内,则会因为在绘制
    时被js阻塞,所以实际上需要等js执行完才会实际完成
    的绘制)

    【Style Sheets】

    相关标签跳过此步骤

    如果当前解析结果为css相关标签,则等待其CSS资源加载完成,同时继续进行下一行的【HTML Parser】

    【CSS Parser】

    相关标签跳过此步骤

    当CSS资源加载完毕后,对CSS从上到下逐行解析

    【Style Rules】

    相关标签跳过此步骤

    当CSS解析完毕后,生成CSS规则树,也叫CSSOM,也就是window.document.styleSheets

    【Attachment】

    根据DOM树与CSS规则树计算出每个节点的具体样式。
    分为两种情况
    1. 如果当前节点为
    相关节点
    如果HTML从未解析到过css相关标签则使用HTML默认样式,如果已经解析到过css相关标签则阻塞等待css标签也完成【Attachment】步骤后才进入下一步。
    2. 如果当前节点为css相关节点
    则需要根据是否在之前已经渲染过CSS资源中对应的DOM节点,如果已经渲染过则需要重绘。如果未渲染过任何相关DOM节点则此步骤为最后一步。

    【Render Tree】

    生成渲染树,在此阶段已经可以将具体的某个
    与对应的CSS样式对应起来了。有了渲染树后浏览器就能根据当前浏览器的状态计算出某个DOM节点的样式、大小、宽度、是否独占一行等信息。计算完成后把一些不需要显示出来的节点在渲染树中删掉。如display: none。

    【Layout】

    通过渲染树进行分层(根据定位属性、透明属性、transform属性、clip属性等)生成图层树。

    【Painting】

    绘制所有图层,并转交给合成线程来最最终的合并所有图层的处理。

    【Display】

    最终生成页面并显示到浏览器上

    浏览器处理每一帧的流程

    浏览器在渲染完页面之后还需要不间断的处理很多内容的,比如动画、用户事件、定时器等。因此当浏览器渲染完页面后,还会在之后的每一帧到来时执行以下的流程。
    2022/05/20 【Web页面全链路性能优化指南】 - 图6
  • 【Input events】处理用户事件,先处理【阻塞事件Blocking】包括touch和wheel事件,后处理【非阻塞事件Non-blocking】包括click和keypress
  • 【JS】处理完用户事件后执行【定时器Timers】
  • 【Begin frame】处理完定时器后开始进行【每帧事件Per frame events】的处理,包括窗口大小改变、滚动、媒体查询的更改、动画事件。
  • 【rAF】处理完帧事件后执行requestAnimationFrame回调函数和IntersectionObserver回调函数。
  • 【Layout】然后【重新计算样式Recalc style】、【更新布局Update layout】、【调整Observer回调的大小Resize Observer callbacks】
  • 【Paint】然后【合成更新Compositing update】、【Paint invalidation】、【Record】

    Chrome性能优化相关工具

    了解完浏览器渲染原理,我们还需要知道根据哪些指标才能判断一个页面性能的好坏,在Chrome中这些指标应该怎么获取。以及Chrome都为我们提供了哪些性能相关的工具,如何使用。

    Chrome Performance(性能)

    Performance既是一个Chrome工具,可用于性能检测。
    同样又是一套JS API,可在Chrome中执行。

    Chrome Performance 工具的使用

    打开Chrome调试面板选择Performance,中文版为性能,点击刷新按钮,Performance会刷新并录制当前页面,然后我们就可以在面板中看到如下的各种性能相关细节。
    2022/05/20 【Web页面全链路性能优化指南】 - 图7

    Performance API介绍

    js中存在Performance API,可用于性能检测,具体如下
    2022/05/20 【Web页面全链路性能优化指南】 - 图8

2022/05/20 【Web页面全链路性能优化指南】 - 图9

  • 【Process Unload Event】等待上一个页面卸载。在我们输入url后浏览器需要卸载上一个页面的内容然后再去执行navigationStart导航开始。
  • 【Redirect】浏览器卸载完上一个页面后会执行redirectStart然后将当前页面重定向到用户新输入的url页面。完成重定向后会执行redirectEnd
  • 【Service Worker Init】如果当前页面注册了Service Worker那么执行workerStart对Service Worker进行初始化操作。
  • 【Service Worker Fecth Event 】浏览器准备好发送请求,在发送之前会执行fetchStart
  • 【HTTP Cache】如果有缓存则直接取缓存,如果没有的话则继续解析
  • 【DNS】如果没有缓存则执行domainLookupStart 然后去解析DNS,解析完会执行domainLookupEnd
  • 【TCP】DNS解析完会执行contentStart,然后进行TCP三次握手,如果是HTTPS则执行secureConnectionStart进行SSL协商。完成后会执行contentEnd。
  • 【Request】TCP连接创建完成后执行requestStart,然后开始真正的发送请求
  • 【Response】请求被响应且首字节返回时会先执行responseStart,响应全部接收完毕后会执行responseEnd
  • 【Processing】响应完执行domLoading开始加载dom,dom加载完毕后执行domInteractive,此时dom已经可以交互。然后执行domContentLoadedEventStart,当dom整个节点全部加载完毕并执行完DOMContentLoaded事件后会触发domContentLoadedEventEnd简称DCL当dom整个加载完成会执行domComplete,此时页面资源已经全部加载完毕。
  • 【onLoad】当页面资源已经全部加载完毕后会执行loadEventStart,触发window.onload事件,load事件完成后会执行loadEventEnd。