渲染原理与性能优化
浏览器渲染原理主要是要我们了解浏览器的渲染过程,从而了解浏览器从URL到页面UI和操作的全过程,方便我们针对不同性能问题做相应的性能优化。浏览器渲染主要包括11(或10)个步骤。
DNS解析
HTTP请求
TLS/SSL(HTTPS协议执行)
解析DOM树(Document Object Tree)和CSSOM树(CSS Object Tree)
计算computed tree
计算layout tree
分层
生成绘制指令
分块
光栅化
GPU绘制
一、 DNS解析
我们一般在浏览器地址栏会输入一个URL,一般情况下输入的是域名而不是主机地址加端口(由于地址并不好记忆,并且不具备实际意义,所以一般需要使用更具有实际意义的域名来书写URL)
,拿到域名后需要想DNS服务器发起请求并解析对的的IP地址以便后续建立TCP连接。
性能优化:
DNS解析还是比较耗时可以使用DNS缓存来从本地获取域名对应的IP地址,可以使用本地的host进行DNS缓存。
示意图:
二、 HTTP请求
在客户端获取到域名对应的IP地址后就会建立HTTP请求,HTTP协议为应用层协议,会通过传输层协议TCP协议建立TCP安全通道,期间会经历3次握手和4次挥手。
性能优化:
- 缓存优化
缓存优化是利用浏览器缓存进行网络优化,主要包含强制缓存和协商缓存。可直接从内存缓存(memory cache)和磁盘缓存(disk cache)取出资源而不经过网络。
2. HTTP协议优化
目前主要在使用的HTTP协议包含两种:HTTP1 与 HTTP2。HTTP2相对HTTP1做了性能优化。主要包含以下部分:
a. 传输优化,HTTP1 传输文本,HTTP2 传输二进制。
b. 并发优化,HTTP2 相对HTTP1 做了并发优化,同一时间可以发送更多HTTP请求。
c. Header压缩,HTTP2 对请求头进行压缩,减小请求同空间。
三、TLS/SSL(HTTPS协议执行)
TLS协议是传输层加密协议,也是HTTPS为什么比HTTP相对安全的关键。TLS协议会经历5次握手建立加密通道。
四、解析DOM树(Document Object Tree)和CSSOM树(CSS Object Tree)
在客户端真正拿到html文件后,浏览器就会开启主线程读取html文本,并开始解析,将html文本解析成DOM Tree(Documnet Object Module Tree)和CSSOM Tree(CSS Object Tree)。解析过程中会遇到script标签可能会阻塞主线程,阻塞主线程主要是因为,js代码可能会去操作DOM改变DOM Tree。
性能优化:
script 加defer 或 async (src 和 href的区别)
webpack分包
css 的 href 和 @import
减少DOM和CSSOM操作
五、计算computed tree
将DOM Tree 和 CSSOM Tree进行合并,计算出每个DOM的样式的最终计算结果
六、计算layout tree
LayoutTree并不和computerTree相同,它是将真正需要渲染的DOM进行处理好后生成的渲染树,处理主要包括:移除不可见元素(如:display:none);行盒和块盒不可相邻,增加匿名行盒和匿名块盒(视觉格式化模型相关知识),可以在chrome浏览器控制台的Element页签的Layout子页签看到LayoutTree的一些信息。一旦改变了地理位置信息就会进行冲洗布局,重新计算Layout Tree,也就是我们常说的回流。
性能优化:
- 减少或者避免改变地理位置信息(如:margin, padding, border, fontsize等),防止回流。
七、分层
分层主要是将layoutTree进行计算得到分层模型一般由浏览器算法完成(如:滚动条默认单独一层),也可以通过配置position: fixed / absolute 和 z-index进行分层(css 叫 层叠样式表的由来),在chrome控制台中会有Layers页签查看分层模型,每层之间互不影响相互独立。
性能优化:
- 可以对地理位置信息经常发生改变的元素单独分层减少其他DOM的回流
八、生成绘制指令
这一步会生成绘制指令,我们常说的重绘也就是发生在这一步,生成想canvas的绘制指令类似的指令,控制画笔位置。
九、分块(Tiling)
这一步相当于把页面分块,靠近浏览器可视区域的块会优先渲染,提高用户的体验。
如下图,会以1=>2=>3的顺序进行渲染:
十、光栅化
这一步会将需要渲染的块转化成GPU指令,为GPU渲染做准备。
十一、GPU绘制
这一步会将GPU指令交给GPU进行绘制。
性能优化:
- transform 发生在这一步,GPU的计算速度远远高于CPU的的算速度,transform本质是进行矩阵计算,可以避免之前的所有计算过程重新计算,所以在有必要的时候尽量使用transform。