display:none和visibility:hidden的区别,引发重排、重绘、合成阐述

5,462 阅读5分钟

一、display:none和visibility:hidden的区别

1、display:none隐藏完元素所占据的空间也会消失 。visibility:hidden隐藏完元素还是占据空间。

2、display:none没有继承性,visibility:hidden有继承性。给父元素设置visibility:hidden,其子元素也会继承该属性,如果给子元素设置visibility:visible,则子元素会显示出来。

3、visibility:hidden不会影响计数器的运算,比如ol标签中的li标签,隐藏其中的第二个标签,后续标签的序号不变,而如果使用display:none后续标签的序号会衔接上去上一个。

4、CSS3的transition支持visibility属性,但是并不支持display

5、 display:none会引起回流(重排)和重绘 visibility:hidden会引起重绘 

二、提交文档和渲染阶段

1、提交文档

请求回来的文档数据保存在网络进程,还没有提交给渲染进程。在提交文档阶段,浏览器进程将网络进程接收到的HTML数据提交给渲染进程。

具体流程:

1.1、首先当浏览器进程接收到网络进程的响应头数据之后,便向渲染进程发起“提交文档”的消息;

1.2、渲染进程接收到“提交文档”的消息后,会和网络进程建立传输数据的“管道”;等文档数据传输完成之后,渲染进程会返回“确认提交”的消息给浏览器进程;

1.3、浏览器进程在收到“确认提交”的消息后,会更新浏览器界面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 Web 页面。

2、渲染阶段

2.1、构建 DOM 树(每个标签对应一个节点,包括display:none被隐藏的节点)

2.2、css解析成样式结构体styleSheets(可以解释成cssom),最后计算得到每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内。

2.3、构建布局树LayoutTree(可以看成16年之前的渲染树),把所有DOM 树中可见节点添加到布局树中,不可见的节点不会添加到布局树里面( dispaly:none)。

2.4、对布局树进行分层,并生成分层树。

2.5、为每个图层生成绘制列表,并将其提交到合成线程。

2.6、合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。

2.7、合成线程发送绘制图块命令 DrawQuad 给浏览器进程。

2.8浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

三、重排、重绘、合成

1、更新了元素的几何属性(重排)

如果你通过 JavaScript 或者 CSS 修改元素的几何位置属性,例如改变元素的宽度、高度等,那么浏览器会触发重新布局,解析之后的一系列子阶段,这个过程就叫重排。无疑,重排需要更新完整的渲染流水线,所以开销也是最大的。

2. 更新元素的绘制属性(重绘)

图中可以看出,如果修改了元素的背景颜色,那么布局阶段将不会被执行,因为并没有引起几何位置的变换,所以就直接进入了绘制阶段,然后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。

3. 直接合成阶段

那如果你更改一个既不要布局也不要绘制的属性,会发生什么变化呢?渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成。具体流程参考下图:避开重排和重绘在上图中,我们使用了 CSS 的 transform 来实现动画效果,这可以避开重排和重绘阶段,直接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源,另外也避开了布局和绘制两个子阶段,所以相对于重绘和重排,合成能大大提升绘制效率。

四、优化(待研究)

1、浏览器队列优化机制

现代浏览器不会对每次回流重绘都进行操作,而是把涉及回流重绘的操作放到一个队列里面,当一段时间后或者队列放满了后,浏览器会一次性将队列中所有的操作执行,这样就会大大的减少回流重绘的次数,提高性能,但是当代码执行过程中需要获取某些元素的位置信息(比如执行到一些js自带获取位置信息的api时),该队列会被强制刷新,这些api有:offsetLeft,offsetTop, offsetWidth, offsetHeight, clientWidth, clientHeigth,clientLeft,clientTop,scrollWidth, scrollHeight, scrollLeft, scrollTop, getBoundingClientRect()...

2、如何减少回流和重绘

css

使用transform来代替top(css硬件加速,相同效果前者少一个layout延时) 常见的触发硬件加速的css属性:transform,opacity,filters,Will-change 避免使用table布局 尽量避免多层嵌套,结构尽量扁平化 对于有复杂动画效果的DOM元素应该将其独立处出来,脱离文档流 使用visibility,opacity 代替 display: none (前者的展现只涉及重绘, 后者需要回流)

js

给一个DOM元素设置css样式避免一行一行单独设置(如这样: el.style.width = '40px', el.style.color = 'red'), 可以用cssText进行集中设置,或者先把样式类在css设置好,通过js直接加类名。 涉及批量修改DOM可分为三个步骤

(1): 使要修改的DOM元素脱离文档流。

(2): 对其进行多次修改。

(3): 将DOM元素带回文档流。

针对上述的涉及批量修改DOM的方法大概有种如下:

(1)先将元素的display设置为none,然后修改,最后将其重新显示

(2)使用文档片段(createDocumentfragment())在当前文档流外构建一个子树,操作完再将其插入回文档(一般用来涉及DOM元素的增加)

(3)将原始元素拷贝到一个脱离文档的节点中,修改节点后,再替换原始的元素(api: 拷贝 cloneNode(), 替换 replaceChild())