Open includeios opened 5 years ago
您好,非常喜欢你写的文章。对于渲染流程可以分为三种这我有一个疑问,第二和第三种都是没有布局的,那么浏览器是怎么知道元素的具体位置的呢?
您好,非常喜欢你写的文章。对于渲染流程可以分为三种这我有一个疑问,第二和第三种都是没有布局的,那么浏览器是怎么知道元素的具体位置的呢?
页面在首次打开时一定会进行第一种类型的渲染,布局计算后会将具体的计算信息保存在内存中,之后判断不需要重新计算的话直接拿内存里的计算就好o( ̄▽ ̄)ブ
是不是普通文档流默认就是一个复合层,然后图层经过合成线程和光栅化线程处理成位图之后,会交给GPU进行合成渲染,最终呈现在页面?
浏览器的渲染过程2.0 — Composite
前言
上一篇文章: 《浏览器的渲染机制》里,大概介绍了浏览器从下载完第一份document文件,到渲染出整个页面所发生的事情(也称作:
关键路径渲染
),其中包括:1、构建对象模型(DOM,CSSOM),2、构建渲染树(RenderTree),3、布局,4、渲染。这一篇文章我们将参考chrome浏览器的内核WebKit, 详细介绍下浏览器呈现引擎的一些概念和渲染中的一个关键环节:Composite(渲染层合并),它所做的事情和带来的效果。
浏览器的主要结构
市面上主流的几款浏览器,基本上都是由下面几个结构组成:
其中值得注意的是,与其他浏览器不同的是:chrome浏览器中每个tab页对应一个呈现引擎实例,每个tab页都是一个单独的进程。
呈现引擎
webkit的呈现引擎,官方提供的一张主流程图是:
其中有些名词和 《浏览器的渲染机制》里提到的不一样,比如:Style Rules,这是webkit自己引入的几个概念,这里先解释一下,方便大家理解:
DOM Tree:浏览器将HTML解析成树形的数据结构。
Style Rules:浏览器将CSS解析成树形的数据结构,对应我们的CSSOM。
Render Tree:DOM和CSSOM合并后生成Render Tree。
layout: 布局。有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从 属关系,从而去计算出每个节点在屏幕中的位置。
painting: 渲染。按照算出来的规则,通过显卡,把内容画到屏幕上。
reflow(回流/重排):当浏览器发现某个部分发生了点变化影响了布局,需要倒回去重新渲染,这个回退的过程叫 reflow。
repaint(重绘):改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸没有变。
上面的过程可以合并成:
实际场景下,渲染流程可以分为下面三种,layout和point这个过程是可以避免的:
显而易见,第三种渲染流程浏览器的处理是最快的,那什么情况下的渲染是第三种情况呢,这里就不得不提到Compsite了
Compsite
上一篇我们提到,浏览器渲染时,对渲染树有着一个RenderObject -> RenderLayout的转换过程:
实际上,Chrome拥有两套不同的渲染路径:硬件加速路径和旧软件路径。
硬件加速路径会将一些图层的合成交给GPU处理,比CPU处理更快,而我们的RenderLayout(有些地方又称作PaintLayers)并不能作为GPU的输入,这里会将RenderLayout再转换成GraphicsLayers:
很绕口对不对= =,人性话来说,与RenderObject转换成RenderLayer一样,浏览器也是根据“某些规则”,选中一些特殊的RenderLayout节点(天选之子),这些节点将被称为Compositing Layers,Compositing Layers与其他的普通节点不一样的是他们拥有自己的GraphicsLayer,而那些没有被选中的节点,会和父层公用一个GraphicsLayer。
每个GraphicsLayer都拥有GraphicsContext,GraphicsContext输出的位图会作为纹理上传到GPU中。
什么是纹理?可以把它想象成一个从主存储器(例如 RAM)移动到图像存储器(例如 GPU 中的 VRAM)的位图图像(bitmapimage)
Chrome 使用纹理来从 GPU上获得大块的页面内容。通过将纹理应用到一个非常简单的矩形网格就能很容易匹配不同的位置(position)和变形(transformation)。这也就是3DCSS 的工作原理,它对于快速滚动也十分有效。
RenderLayer -> GraphicsLayer,合成层的创建标准
合成层的性能优化:
对于提升为合成层有以下几个好处:
注意:
总结来说,一般一个元素开启硬件加速后会变成合成层,可以独立于普通文档流中,改动后可以避免整个页面重绘,提升性能。
那么针对上面这些特点,可以做的性能优化有:
提升动画效果的元素
合成层的好处是不会影响到其他元素的绘制,因此,为了减少动画元素对其他元素的影响,从而减少paint,我们需要把动画效果中的元素提升为合成层。
提升合成层的最好方式是使用 CSS 的
will-change
属性。从上一节合成层产生原因中,可以知道 will-change 设置为opacity、transform、top、left、bottom、right
可以将元素提升为合成层。对于不支持will-change的浏览器版本,可以加上transform属性,比如:transform: translateZ(0)
使用 transform 或者 opacity 来实现动画效果
上文提到transform和opaciry属性不会引发layout和paint,值得注意的是,只有元素提升到合成层后才不会引发,普通元素修改transform和opacity还是会引发的。
减少绘制区域
对于不需要重新绘制的区域应尽量避免绘制。
比如一个 fix 在页面顶部的固定不变的导航header,当页面内容某个区域 repaint 时,在没有提升为合成层时,整个屏幕包括 fix 的 header 也会被重绘。而对于固定不变的区域,我们期望其并不会被重绘,因此可以通过之前的方法,将其提升为独立的合成层。
如何查看合成层
然后页面的合成层会用黄色的边框框出来
Chrome DevTools -> Layers
左边会详细列出页面的所有合成层,点击时会将这个合成层在页面的具体位置显示出来。下面的Details和Profiler会显示这个合成层的详细信息(大小,加载时间等...)
合成层可能会遇到的问题
由于合成层本身是占用内存的,当浏览器出现大量无法被压缩的合成层时(层爆炸),会导致内存紧张,GPU资源过度消耗等问题,具体可参考CSS3硬件加速也有坑!!!,这篇文章写的。
参考资料
https://fed.taobao.org/blog/2016/04/26/performance-composite/
https://www.html5rocks.com/zh/tutorials/speed/layers/
https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/#Global_and_Incremental
https://segmentfault.com/a/1190000014520786
https://div.io/topic/1348
https://developers.google.com/web/fundamentals/performance/rendering/stick-to-compositor-only-properties-and-manage-layer-count