共计 6040 个字符,预计需要花费 16 分钟才能阅读完成。
在日常的开发中,通过应用 css 属性,做一些动效、动画时,会发现在页面有卡顿;
在 Android 低端机尤为显著,故须要晓得浏览器渲染以及优化伎俩
浏览器渲染流程
- 构建 DOM 树:浏览器将 HTML 解析成树形构造的 DOM 树,一般来说,这个过程产生在页面首次加载,或页面 JavaScript 批改了节点构造的时候。
- 构建渲染树:浏览器将 CSS 解析成树形构造的 CSSOM 树,再和 DOM 树合并成渲染树。
- 布局(Layout):浏览器依据渲染树所体现的节点、各个节点的 CSS 定义以及它们的从属关系,计算出每个节点在屏幕中的地位。Web 页面中元素的布局是绝对的,在页面元素地位、大小发生变化,往往会导致其余节点联动,须要从新计算布局,这时候的布局过程个别被称为 回流 / 重排(Reflow)。
- 绘制(Paint):遍历渲染树,调用渲染器的 paint() 办法在屏幕上绘制出节点内容,实质上是一个像素填充的过程。这个过程也呈现于回流或一些不影响布局的 CSS 批改引起的屏幕部分重画,这时候它被称为 重绘(Repaint)。实际上,绘制过程是在多个层上实现的,这些层咱们称为 渲染层(RenderLayer)。
- 渲染层合成(Composite):多个绘制后的渲染层依照失当的重叠程序进行合并,而后生成位图,最终通过显卡展现到屏幕上。
以上是浏览器渲染的根本步骤,简述:
DOM tree + CSS tree == Render tree ==> Layout tree ==> PaintLayer => Composite
PaintLayer => Composite
只有页面上元素有扭转,都会反复渲染以上局部步骤,在一帧内实现(16.77ms);
通过设置 css 款式,做动画,在一帧内无奈渲染完,甚至来不及渲染,就会卡顿;
其中局部渲染卡顿,跟渲染层合成(Composite)有关系。
Composite
什么是渲染层合成
在 DOM 树中每个节点都会对应一个渲染对象(RenderObject),当 它们的渲染对象处于雷同的坐标空间(z 轴空间)时,就会造成一个 RenderLayers,也就是渲染层。渲染层将保障页面元素以正确的程序重叠,这时候就会呈现层合成(composite),从而正确处理通明元素和重叠元素的显示,这步叫做“层叠上下文”
这个模型相似于 Photoshop 的图层模型,在 Photoshop 中,每个设计元素都是一个独立的图层,多个图层以失当的程序在 z 轴空间上叠加,最终形成一个残缺的设计图。
对于有地位重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并程序出错,将会导致元素显示异样。
渲染对象(RenderObject)
一个 DOM 节点对应了一个渲染对象,渲染对象仍然维持着 DOM 树的树形构造。
渲染层(RenderLayer)
这是浏览器渲染期间构建的第一个层模型,处于 雷同坐标空间(z 轴空间)的渲染对象,都将归并到同一个渲染层中 ,因而依据层叠上下文,不 同坐标空间的的渲染对象将造成多个渲染层,以体现它们的层叠关系。
因而满足造成层叠上下文条件的 RenderObject 肯定会为其创立新的渲染层,当然还有其余的一些非凡状况,为一些非凡的 RenderObject 创立一个新的渲染层,比方 overflow != visible 的元素。依据创立 RenderLayer 的起因不同,能够将其分为常见的 3 类:
- NormalPaintLayer
- 根元素(HTML)
- 有明确的定位属性(relative、fixed、sticky、absolute)
- 通明的(opacity 小于 1)
- 有 CSS 滤镜(fliter)
- 有 CSS mask 属性
- 有 CSS mix-blend-mode 属性(不为 normal)
- 有 CSS transform 属性(不为 none)
- backface-visibility 属性为 hidden
- 有 CSS reflection 属性
- 有 CSS column-count 属性(不为 auto)或者 有 CSS column-width 属性(不为 auto)
- 以后有对于 opacity、transform、fliter、backdrop-filter 利用动画
- OverflowClipPaintLayer
- overflow 不为 visible
- NoPaintLayer
- 不须要 paint 的 PaintLayer,比方一个没有视觉属性(背景、色彩、暗影等)的空 div。
满足以上条件的 RenderObject 会领有独立的渲染层,而其余的 RenderObject 则和其第一个领有渲染层的父元素共用一个。
图形层(GraphicsLayer)
渲染层,和其第一个领有 GraphicsLayer 的父层共用一个。
每个 GraphicsLayer 都有一个 GraphicsContext,GraphicsContext 会负责输入该层的位图,位图是存储在共享内存中,作为纹理上传到 GPU 中,最初由 GPU 将多个位图进行合成,而后 draw 到屏幕上,此时,咱们的页面也就展示到了屏幕上。
合成层(CompositingLayer)
满足某些非凡条件的 渲染层,会被浏览器主动晋升为合成层。合成层领有独自的 GraphicsLayer,
个渲染层满足哪些非凡条件时,能力被晋升为合成层呢?这里列举了一些常见的状况:
- 3D transforms:translate3d、translateZ 等
- video、canvas、iframe 等元素
- 通过 Element.animate() 实现的 opacity 动画转换
- 通过 СSS 动画实现的 opacity 动画转换
- position: fixed
- 具备 will-change 属性
- 对 opacity、transform、fliter、backdropfilter 利用了 animation 或者 transition
常见实用**transforms:translate3d、translateZ **
来造成合成层,这样是对原先的布局没有影响
示例
其余合成状况
隐式合成
除此之外,在浏览器的 Composite 阶段,还存在一种隐式合成,局部渲染层在一些特定场景下,会被默认晋升为合成层。其实就是元素 div 之间互相重叠
上图所示:把 position-1
生成合成层,前面的元素会互相重叠,为了层叠上下文展现正确;导致前面的元素间接 隐式合成了;
合层压缩
如上图,目前没有遇到
合成爆炸
通过上图钻研,很容易就产生一些不在预期范畴内的合成层,当这些不合乎预期的合成层达到一定量级时,就会变成层爆炸。
须要排查是否有重叠,是否通过 z-index
来调节 层叠程序
优化
长处
- 合成层的位图,会交由 GPU 合成,比 CPU 解决要快得多;
- 当须要 repaint 时,只须要 repaint 自身,不会影响到其余的层;
- 元素晋升为合成层后,transform 和 opacity 才不会触发 repaint,如果不是合成层,则其仍然会触发 repaint。(只有 css 动画,才能够,js 去管制还是会重排、重绘的)应用 transform 或者 opacity 来实现动画成果
更新元素几何属性(产生重排)
浏览器会触发从新布局,解析之后的一系列子阶段,这个过程就叫重排 。无疑,重排须要更新残缺的渲染流水线,所以开销也是最大的
引发重排的状况
- 页面首次渲染;
- 浏览器窗口大小发生变化;
- 元素的内容发生变化;
- 元素的尺寸或者地位发生变化;
- 元素的字体大小发生变化;
- 激活 CSS 伪类;
-
查问某些属性或者调用某些办法;
- offsetTop、offsetLeft、offsetWidth、offsetHeight
- scrollTop、scrollLeft、scrollWidth、scrollHeight
- clientTop、clientLeft、clientWidth、clientHeight
- width、height
- getComputedStyle()
- getBoundingClientRect()
- 增加或者删除可见的 DOM 元素。
更新元素绘制属性(产生重绘)
如果批改了元素的背景色彩,那么布局阶段将不会被执行,因为并没有引起几何地位的变换,所以就间接进入了绘制阶段,而后执行之后的一系列子阶段,这个过程就叫重绘。相较于重排操作,重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些 。
引发重绘的状况
- 重排
- visibility: visible <=> hidden
- 色彩扭转
- 其余几何变动 …
间接合成阶段
咱们应用了 CSS 的 transform 来实现动画成果, 这能够避开重排和重绘阶段,间接在非主线程上执行合成动画操作。这样的效率是最高的,因为是在非主线程上合成,并没有占用主线程的资源 ,另外也避开了布局和绘制两个子阶段,所以绝对于重绘和重排,合成能大大晋升绘制效率。
毛病
- 绘制的图层必须传输到 GPU,这些层的数量和大小达到一定量级后,可能会导致传输十分慢 ,进而导致一些 低端和中端设施上呈现闪动;
-
隐式合成容易产生适量的合成层,每个合成层都占用额定的内存,而内存是挪动设施上的贵重资源,过多应用内存可能会导致浏览器解体,让性能优化适得其 s
其余优化
css
- 应用 transform 代替 top
- 应用 visibility 替换 display: none,因为前者只会引起重绘,后者会引发回流(扭转了布局
- 防止应用 table 布局,可能很小的一个小改变会造成 整个 table 的从新布局。
- 尽可能在 DOM 树的最末端扭转 class,回流是不可避免的,但能够缩小其影响。尽可能在 DOM 树的最末端扭转 class,能够限度了回流的范畴,使其影响尽可能少的节点。
- 防止设置多层内联款式,CSS 选择符从右往左匹配查找,防止节点层级过多。
- 将动画成果利用到 position 属性为 absolute 或 fixed 的元素上,防止影响其余元素的布局,这样只是一个重绘,而不是回流,同时,管制动画速度能够抉择 requestAnimationFrame,详见探讨 requestAnimationFrame。
- 防止应用 CSS 表达式,可能会引发回流。
-
将频繁重绘或者回流的节点设置为图层,图层可能阻止该节点的渲染行为影响别的节点,例如 will-change、video、iframe 等标签,浏览器会主动将该节点变为图层。
js:
- 防止频繁操作款式,最好一次性重写 style 属性,或者将款式列表定义为 class 并一次性更改 class 属性。
- 防止频繁操作 DOM,创立一个 documentFragment,在它下面利用所有 DOM 操作,最初再把它增加到文档中。
- 防止频繁读取会引发回流 / 重绘的属性,如果的确须要屡次应用,就用一个变量缓存起来。
-
对具备简单动画的元素应用相对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。
总结
在做动画时尽可能的应用应用 csstransforms 属性做动画,,它跳过 Layout 和 Paint 阶段,间接进入渲染,所以会晋升局部性能,同时看看所属空间地位,是否晋升了 合成层; 还有留神元素是否重叠,避免合成层爆炸;这样至多能保障 css 方面,尽可能性能没有问题。
剩下的请查看其余优化伎俩
示例代码
重叠
<style>
.ball-running,.ball-running1 {
width: 100px;
height: 100px;
animation: run-around 4s linear alternate 100;
background: red;
position: absolute;
border-radius: 50%;
}
@keyframes run-around {
0% {
top: 0;
left: 0;
}
25% {
top: 0;
left: 200px;
}
50% {
top: 200px;
left: 200px;
}
75% {
top: 200px;
left: 0;
}
}
.position-1,.position-2,.position-3,.position-4 {
position: absolute;
width: 100px;
height: 100px;
background-color: red;
border: #1b1b1b 1px solid;
}
.position-1 {transform: translateZ(10px);
}
.position-2 {
top: 90px;
left: 90px;
// transform: translateZ(10px);
}
.position-3 {
top: 180px;
left: 180px;
}
.position-4 {
top: 270px;
left: 270px;
}
.parent-1 {
//width: 100px;
//height: 200px;
background: #5b3a13;
//overflow: auto;
//transform: translateZ(10px);
}
.position-5 { }
<style/>
<div class="parent-1">
<div class="position-1">
1
</div>
<div class="position-2">
2
</div>
<div class="position-3">
3
</div>
<div class="position-4">
4
</div>
</div>
动画
<style>
.ball-running,.ball-running1 {
width: 100px;
height: 100px;
// animation: run-around2 4s linear alternate 100;
background: red;
position: absolute;
border-radius: 50%;
}
@keyframes run-around {
0% {
top: 0;
left: 0;
}
25% {
top: 0;
left: 200px;
}
50% {
top: 200px;
left: 200px;
}
75% {
top: 200px;
left: 0;
}
}
//.ball-running {
// width: 100px;
// height: 100px;
// animation: run-around 2s linear alternate 100;
// background: red;
// border-radius: 50%;
//}
//@keyframes run-around {
// 0% {// transform: translate(0, 0);
// }
// 25% {// transform: translate(200px, 0);
// }
// 50% {// transform: translate(200px, 200px);
// }
// 75% {// transform: translate(0, 200px);
// }
//}
</style>
<div class="ball-running"></div>
参考:
transform 和 left 扭转地位的性能区别
GPU Accelerated Compositing in Chrome
浏览器层合成与页面渲染优化
浏览器渲染页面过程与页面优化
无线性能优化:Composite
浏览器渲染过程
渲染流程
Stick to Compositor-Only Properties and Manage Layer Count