从 B 站出发,用 Chrome devTools performance 分析页面如何渲染
页面是如何渲染的?通常会得到“解析 HTML、css 合成 Render Tree,就可以渲染了”的回答。但是具体都做了些什么,却很少有人细说,我们今天就从 Chrome 的性能工具开始,具体看看一个页面是如何进行渲染的,以及进行页面优化时需要关注哪些指标。
以“老二次元”网站 bilibili 为例,我们将通过分析 performance 面板,串联起 Chrome 页面渲染流程,以及页面的部分量化指标的含义,来看页面具体是如何渲染的。
获取performance数据
首先,打开Chrome devTools, 选择 performace面板,点击录制按钮开始录制。
之后为了防止我们分析页面时出现无关的干扰,我们通过以下步骤降低干扰项:
1、打开 Chrome 无痕模式。
2、关闭所有在 Chrome 无痕模式下启用的拓展(如果有的话)。
3、在地址栏输入 www.bilibili.com 前,先打开 devTools,选择 performance 面板,点击录制按钮。
4、在已经录制的情况下,地址栏回车,请求 B 站,大概 10s 后,停止录制。
我们从上到下,将图分成以下几块,如下图所示:
1、控制面板
2、概览面板
3、网络面板
4、Web Vitals
5、线程面板
6、内存面板
7、聚合面板
控制面板
控制面板有 4 部分内容,分别为:
disable javascript samples:启用后会隐藏一些 JS 调用栈的展示。在一些性能较弱的设备例如移动端上,可以开启这项功能。
Network:可以用来模拟各种网络状况。
enableadvanced paint instrumention (slow):启用后 paint 面板会显示与绘制相关事件的更详细的信息。
CPU:可以用来模拟不同的 CPU 性能。
概览面板
概览面板是各项指标的一个概览,包含了 FPS 帧数、CPU 占用、NET 情况、内存使用情况等。
简单举个例子,比如 FPS 帧数可以直观的看出 FPS 的高低,绿色代表低的部分。而 CPU 栏的黄色代表着 js,紫色代表计算样式和布局,绿色代表绘制。
网络面板
网络面板用于展示正在请求中的各部分的组成情况。
Web vitals
Web vitals 是网站的 Web 体验指标,其中包括 LCP(最大内容绘制)、FID(首次输入延迟)、cls (累计布局偏移)等。
线程面板
线程面板用于展示渲染当前页面所使用到的线程,包含有 Main 线程、GPU 线程、Raster 线程、Chrome_ChildIOThread、Compositor 线程等等。其中 Main 线程,就是我们平时说的大部分 js 的运行环境,即主线程。
内存面板
展示 js 内存、GPU 内存、节点数、监听事件数的变化。
聚合面板
当点击主线程中的火焰图时,此面板会显示显示具体包含执行时间、执行组成、调用栈等等的信息集合。
Chrome是如何渲染页面的?
第一个请求
以第一个请求为例,我们来具体看一下 Chrome 是如何进行页面渲染的?依然是以对 https://www.bilibili.com 的请求为例,来看一下 1ms 的 performance 面板,即下图中红线部分、中间 NET 栏蓝色细长条开始的部分和 Network 中水平箱线图开始的部分。
其中两边横线中间深浅色方框的部分是水平箱线图,是用来展示某部分在整体中的比例关系。比如我们看到这个长长的箱型图,通过直观感受,就能知道对前面一部分横线挺长的,蓝色部分里浅色部分很长,深色的短,右边的横线几乎看不到。那这些又分别能展示什么信息?
首先,点开箱型图最下方的聚合面板(Summary),上面赫然写着:此乃页面源。欲求小破站, 终生皆让我……耗时一秒半。
然后在 Network tab 里查看该请求的 timing 部分,可以得到如下图:
这里的各个部分分别代表:
- Queueing(排队):浏览器会在一些情况下让请求排队等待,比如这个请求的优先级不高,有更高优先级的请求存在;在使用 HTTP/1.0 或者 HTTP/1.1 时,同域请求最大并发数量为 6 个,此时已经达到了最大值;而上图中的请求是属于最高优先级的第一个请求,即浏览器正在硬盘缓存中分配空间,从图上可以看到有 14.72ms 用于在磁盘缓存中分配空间。
Stalled(停顿):它可能会因为上述排队中的任何原因而停顿。
DNS lookup(DNS 查询):解析这个域名的IP地址。需要注意的是,当我们多次访问同一域名时,这部分不会出现在 timing 中。
Initial connection(初始连接):浏览器建立连接,包括 tcp 三次握手、重试以及协商 SSL。图中的紫色部分,就代表了在初始连接过程中的 SSL 协商部分。
Request sent(发送请求):正在发送请求
Waiting (TTFB) 等待第一字节时间:浏览器在等待第一个响应的字节,TTFB 即 Time To First Byte。这个时间包括一个往返的延迟和服务准备响应的时间之和。
Content Download (内容下载):浏览器正在接收响应,浏览器可以通过网络或者 serviceWorker 来直接接收。这个值是读取响应体的总时间。由于网络不佳或者浏览器正在忙于执行其他工作而延迟了对响应体的读取,读取的时间可能会比预期的要长。
这里相信已经有小伙伴注意到了,当浏览器忙于其他事情时也会让读取时间变长。也就是说,当你的 js 把主线程长期占据的时候,就会影响 content download。
下图是 Network 下的对应资源的 waterfall:
现在我们回到最开始说的各色横条上,在水平箱线图中左上角的深蓝色小方块代表着这个请求有着更高的优先级。遇到有浅蓝色的,则表示较低优先级。同时左边横线对应 Network 面板中显示的 Request Sent之前的所有事情的时间。浅色的 bar对应 Network 中 Request Sent 和 Waiting(TTFB)的时间。深色的 bar对应 Network中Content Download 的时间。右边的横线表示等待主线程所花费的时间,在 Network 面板中没有体现。
此外,可能还有些同学注意到,在蓝色箱线图上面还可以看到还有几个灰色的箱线图。不是说www.bilibili.com 是页面的第一个请求吗,难道它之前还有请求?
事实上,这个灰色箱线图相当于上一个页面的结束。如果我们是通过重新录制的方式记录 performance,那就会经历页面刷新的过程。而这几个灰色的其实就是页面刷新 unload 时发起的,是 bilibili 用来记录页面卸载时的一些数据。
说回到箱线图,可以看到在 summary 中显示 Duration 1.08 s (822.88 ms Network transfer + 260.20 ms resource loading)。这个的意思是 260ms 的时间是在 resource loading ,这里resource loading所花费的时间其实就是箱线图右侧的那条横线,等待主线程的时间。
而在 main 进程中,有横线结束的地方,可以看到解码的数据 138,933 Bytes。
这里就出现了几个问题:为什么Encode Data 33479 bytes 算下来是 33479/1024 = 32.69 k,而不是前面 Network 面板里的 33.5k ? 而且 Decode body 138993/1024 = 135.7k 也不是前面的 139k?缺少的一部分数据是什么呢?
为了验证这个问题,需要清空过去所有请求记录,重新点击录制,录制完成后,导出网络请求的 HAR 文件。使用 vscdoe 打开 json 格式的 HAR 文件,寻找 GET https://www.bilibili.com/ content-Type: text/HTML 的那个请求。经过前后的文件对比,找到了这个请求的 response content:
可以看到,图上的 size 有140682 字节。text则是 base64 编码的 HTML 内容,已经被 decode 过。需要注意的是,这里的 decode 不是对 base64 的 decode,是对 gzip 的 decode。
而在这个 text 内容之后,还有一段如下内容:
其中的 _transferSize: 35593 是网络传输的体积,即传输的体积 35593 和 decode 体积 140682。同时我们在 performance 里的主进程中的 finish loading中可以看到下图数据:
这样一看,二者是相同的。说明这个 HTML 的传输体积就是 35593 Bytes。
那为什么在 Network 面板里,我们看到的是 35.6k transferred over Network 呢?
这是因为在 Network 里展示的体积,不是除以 1024 计算的,而是除以 1000,然后四舍五入后的结果。
不过 Summary 里的 pending for xxx ms,似乎是也是等待主线程的时间,但它又是如何在 performance 体现的。目前,我还没搞清楚,如果有了解的小伙伴欢迎留言讨论~
请求其它资源
言归正传,我们现在获取到了 bilibili 网站的 HTML,接下来就需要对这个 HTML 进行处理。
通过 response header 得到 content-type:html,此时会创建一个个渲染进程,也就是主线程的这个进程。但是可以看到在主线程中的蓝色 parse HTML 之前,已经有很多 set request 被发起了,而且这些 send request 都是 HTML 文档中的一些 js 和 css。
为什么会这样呢?不应该是先解析 HTML,才能知道对哪些资源进行发起请求吗?
在 HTML 中引入的 js,存在修改 Dom 的可能,所以浏览器一般在遇到 script 标签后,会先暂停 HTML 解析,优先 js 的下载和执行。但是下载是相对耗时的,如果因为下载时间久而卡住了页面解析,很容易导致用户体验变差,因此 Chrome 采用了一些优化策略。
具体来说,就是当 Chrome 渲染引擎接收到 HTML 的字节流时候,会开启一个专门用来分析字节流中所包含 js、css 文件的预解析线程。解析到相关信息之后,预解析线程会提前开始下载这些资源文件,这样在需要使用的时候就可以直接执行,避免了下载的等待时间。
但是也能观察到,在Parse HTML蓝色方块下方,还有一些 send request,这些怎么就不是提前下载的呢?
我的理解是,这些资源其实都是在预解析线程下载的,尽管在时间上会存在重叠,但和主线程不属于同一个线程,所以 performance 工具会这么显示。但这又带来了另一个问题,为什么有些 js 明明在 HTML 的后面,却在前面就 send request 了,而有些 link/script 明明写在 HTML 里的前面,却在 performance 里后 send request?
这是跟资源的优先级有关。比如普通的 script 标签引用的资源,普通 link 引用的资源,或是rel=prelaod 或 as="style"预加载的资源,可能会被优先处理。而当资源是 prefetch,或者用 这种方式的,由于优先级低,就会被延后下载。一般的其他资源,则按顺序下载。
回到 Network,可以看到在 www.bilibil.com 的箱线图之后,是一连串 js、css、Webp 资源需要加载的请求被发起了。把鼠标移动到这些箱线图上,会看到上面有优先级 lowest low high highest,这就表示了资源的重要程度。
那么这些资源的优先级是如何评定的?一般来说,访问域名获取的 HTML、 以及预加载资源时as="style",拥有最高优先级。普通的
从 B 站出发,用 Chrome devTools performance 分析页面如何渲染的更多相关文章
- Chrome DevTools & performance optimization
Chrome DevTools & performance ptimization https://www.bing.com https://developers.google.com/web ...
- Chrome DevTools & performance & keywords
Chrome DevTools & performance & keywords performance / 优化性能 https://developers.google.com/we ...
- 黄聪:如何扩展Chrome DevTools来获取页面请求
1. Chrome DevTools Extension 熟悉React的同学,可能对React Developer Tools并不陌生, 刚看到的时候,我也觉得很神奇, 因为React De ...
- 使用Chrome工具来分析页面的绘制状态
Chrome Canary(Chrome “金丝雀版本”)目前已经支持Continuous painting mode,用于分析页面性能.这篇文章将会介绍怎么才能页面在绘制过程中找到问题和怎么利用这个 ...
- [Forward]Improving Web App Performance With the Chrome DevTools Timeline and Profiles
Improving Web App Performance With the Chrome DevTools Timeline and Profiles We all want to create h ...
- chrome devtools的debug相关
搜索ctrl+p:搜索Sources面板中指定的文件:然后在主窗口文件标签右键选择reveal in navigator可以在目录中显示当前文件.ctrl+f:搜索devtool主显示窗口所在文件的指 ...
- Chrome DevTools 调研笔记
1 说明 此篇文章针对Chrome DevTools常用功能进行调研分析.描述了每个功能点能实现的功能.应用场景和详细操作. 2 Elements 2.1 功能 检查和实时更新页面的HTML与C ...
- 【转】chrome devtools protocol——Web 性能自动化
前言 在测试Web页面加载时间时,可能会是这样的: 打开chrome浏览器. 按F12打开开发者工具. 在浏览器上打开要测试的页面 查看开发者工具中Network面板的页面性能数据并记录 或者在开发者 ...
- 全新Chrome Devtool Performance使用指南
运行时性能表现(runtime performance)指的是当你的页面在浏览器运行时的性能表现,而不是在下载页面的时候的表现.这篇指南将会告诉你怎么用Chrome DevToos Performan ...
- chrome devtools tip(2)--自定义代码片段,构建你的工具箱
平常开发中,有些代码片段常常用到的,比如,获取 url 参数,rgb转16进制,打印下当前页面的性能数据,给所有的 span 加个样式, 防抖节流,fetch接口,类似 jquery这样的顺手 选择 ...
随机推荐
- Leetcode-SQL学习计划-SQL入门-1527.患某种疾病的患者【regexp正则表达式匹配】
链接:https://leetcode.cn/problems/patients-with-a-condition/ -- 1527.患某种疾病的患者 -- 链接:https://leetcode.c ...
- 如何用 JavaScript 编写你的第一个单元测试
前言 测试代码是使代码安全的第一步.做到这一点的最好方法之一是使用单元测试,确保应用程序中的每个小功能都能发挥其应有的作用--特别是当应用程序处于边缘情况,比如无效的输入,或有潜在危害的输入. 为什么 ...
- 【机器学习】李宏毅——Recurrent Neural Network(循环神经网络)
假设我们当前要做一个人工智能客服系统,那该系统就需要对用户输入的话语进行辨认,例如用户输入: I want to arrive Taipei on November 2nd 那么该系统就能够辨认出来T ...
- 手动解析word Table模块内容
最近来了一个需求, 需要手动解析word ( 好处就是不需要安装office 以及不会有office解析的线程残留),然后就是可以自定义解析规则,比较方便 比如解析这个word里面的内容: 标题,表格 ...
- python画社交网络图
安装依赖包 pip3 install networkx 在图书馆的检索系统中,关于图书的信息里面有一个是图书相关借阅关系图.跟这个社交网络图是一样的,反映了不同对象间的关联性.利用python画社交网 ...
- 彻底弄懂Javascript模块导入导出
笔者开始学习Javascript的时候,对模块不太懂,不知道怎么导入模块,导出模块,就胡乱一通试 比如 import xx from 'test.js' 不起作用,就加个括号 import {xx} ...
- [深度学习] ncnn编译使用
文章目录 工程 ncnn工程编译使用(cpu) ncnn工程编译使用(vulkan) 参考 工程 ncnn工程编译使用(cpu) 在linux下建立如CMakeLists文件即可编译生成ncnn工程 ...
- UOJ33 [UR#2] 树上 GCD
UOJ33 [UR#2] 树上 GCD 简要题意: 给定一棵有根树,对于每个 \(i \in [1,n)\),求出下式的值: \[Ans[i] = \sum_{u<v} \gcd({\rm{di ...
- Spring项目中用了这种解耦模式,经理对我刮目相看
前言 不知道大家在项目中有没有遇到过这样的场景,根据传入的类型,调用接口不同的实现类或者说服务,比如根据文件的类型使用 CSV解析器或者JSON解析器,在调用的客户端一般都是用if else去做判断, ...
- python之路33 MySQL 1
存取数据的演变 1.文本文件 文件路径不固定:C:\aaa.txt D:\bbb.txt E:\ccc.txt 数据格式不统一:jason|123 jason$123 jason 123 2.软件开发 ...