前端性能优化(中)
性能优化调研系列文章
《前端性能优化(上)》 主要说明了:
- 为什么要进行前端性能优化?
- 如何衡量前端性能?
这一篇主要记录了浏览器的加载、渲染过程的调研结果和理解。(待详细梳理 提供动画版本)
浏览器加载、渲染过程
有一道经典的前端面试题: 从输入一个url到页面展示发生了什么? 涉及到计算机网络、操作系统、web等各个方面,从软件到硬件,从协议到实现,每一个点都可以展开深入的学习,所以很考察面试者的综合能力和某一个方面的深入掌握程度。
关于这部分知识,可以参考李兵老师的《浏览器原理》 课程介绍,下面的内容很大一部分 都是引用他的课程内容;另外《浏览器是如何工作的》也非常深入的介绍了浏览器工作的原理。
如果你想要有更深入的理解和掌握,建议你跟着大神一起来实现一个简单的浏览器引擎
不闻不若闻之 闻之不若见之 见之不若知之 知之不若行之 学至于行而止矣 -- 荀子
浏览器架构
浏览器渲染流水线 进程间通信 渲染主线程浏览器导航
image图片来源: 极客时间
- 浏览器主进程处理用户输入
- 浏览器主进程 通知网络进程 发起真正的请求
- 网络进程接收到请求头信息之后 发送给浏览器主进程
- 浏览器主进程 接收到网络进程发送的头消息之后 发送“提交导航 (CommitNavigation)”消息到渲染进程
- 渲染进程 收到“提交导航”消息之后,直接和网络进程建立数据管道,接受html数据
- 渲染进程接收html数据完成之后,会通知浏览器主进程:确认提交
- 主进程收到渲染进程的“确认提交”消息之后,就开始更新浏览器的状态:loading、前进、后退、url、当前页面
以上 整个导航流程就走完了。
浏览器渲染流水线
一旦渲染进程发送 “确认提交”给浏览器主进程,就会开始解析页面加载子资源。
-
html parsing
图片来自google 文档- 解析html -
生成DOM树
图片来源 google文档- 生成DOM树 -
样式计算:
Recaculate Style
将样式信息(来自内联样式、style、link等)转换成styleSheets
;
Recaculate Style
使用所有通过css parser
解析得到的style rules
,包括浏览器给出的默认样式,计算出每一个DOM元素最终的style
的值,存储在ComputedStyle
中。
- 布局阶段: 生成
LayoutTree
经过样式计算阶段之后,DOM-Tree
中的节点都有了自己的样式信息,但是还不知道如何排版,放到哪个位置。
布局阶段,就是确定绘制区域的位置和大小,相对来说也是比较复杂。- 对于
block flow
布局来说相对简单,从上往下依次排列就好
block flow -
inline block
inline-block - 其他更复杂的排版:文字、float、flex、table等等
- 对于
总结来说,经过了布局阶段之后,就生成了一个
layout treeLayoutTree
-
paint
google文档-绘制- 我们已经通过布局阶段,得到了一棵
LayoutTree
,现在我们已经知道了每一个Layout object
的布局、显示信息 - 分层 生成图层数LayerTree:z-index、3D转换、 z-index 等会影响布局对象的显示顺序(层级)请参考 《层叠上下文》
分层 - 根据
LayerTree
,产出一个线性的绘制对象列表(列表中的每一个元素存放着绘制的显示对象和对应的绘制操作) - 对于单独的一个
Layout Object
,可能会包含多个显示对象(Display Item
)(如果你使用过canvas
来绘制一些东西或者使用过一些游戏引擎对这个应该比较熟悉)
- 我们已经通过布局阶段,得到了一棵
输出的display item list
,会作为合成线程的输出
current display item list: [
{
"chunk": "LayoutBlockFlow DIV id='example'
0x1b94c141c0:LayoutBlockFlow DIV id='example':DrawingPaintPhaseSelfBlockBackgroundOnly:0",
"state": "t:0x3e697bca90 c:0x3e697ac8d0 e:0x3e697ac290",
"displayItems": [
{
"index": 0,
"clientDebugName": "InlineTextBox 'hi'",
"id": "0x1605c60410:InlineTextBox 'hi':DrawingPaintPhaseForeground:0",
"visualRect": "401,357 72x20",
"opaque": false,
"record": [
{
"method": "drawTextBlob",
"params": {
"x": 401.5,
"y": 373,
"paint": {
"color": "#FF333333",
"strokeWidth": 0,
"strokeMiter": 4,
"flags": "AntiAlias",
"filterLevel": "Low",
"strokeCap": "Butt",
"strokeJoin": "Miter",
"styleName": "Fill"
}
}
}
]
}
]
}
]
- 光栅化
- 渲染主线程提交 绘制列表给 合成线程;
- 合成线程将图层划分为图块(256256 或者 512512)
- 合成线程将图块提交给 栅格化线程池
- 渲染主进程 通过栅格化线程 发送图块生成位图的指令发送给GPU,
- GPU 生成图块的位图,保存在GPU内存中
- 一旦所有图块都被光栅化 合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程
- 浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上
图片来源:极客时间
几个关于渲染流水线的问题
待补充示例demo
- CSS 外链下载会阻塞 DOM 的构建吗?
不会, 参考demo
-
CSS 外链下载会阻塞 布局树的构建吗?
会 参考demo -
CSS外链文件下载会阻塞后面的js的下载吗?
不会,现代浏览器会在收到html文档之后预解析,请求所有的资源
image.png -
CSS外链文件下载会阻塞js的执行吗?
会
css的下载阻塞了js的执行 -
js文件的下载和执行会阻塞 DOM树的构建吗?
会