本文作者:溯流
1. 前言
浏览器与前端开发的关系显而易见,而理解浏览器的渲染原理,能够帮忙咱们晋升页面性能,解决一些渲染上的问题。最近在开发一个挪动端 H5 页面的时候,就遇到一个奇怪的问题,有一个榜单页面在最新版本 IOS 手机上切换 tab 的时候,左上角的倒计时呈现闪动,咱们来看一些成果。
大略的代码构造
通过插件查看了一下 DOM 构造失常,款式也和其余手机上统一,那问题出在哪里呢?我想大概率是最新版本 IOS 浏览器渲染的问题。说到这种渲染问题,我第一工夫想到的就是用 GPU 渲染晋升为合成层试试,于是我给倒计时的 DOM 加上了简短的一行代码 will-change:transform
,问题顺利解决,倒计时模块的渲染不在受其余内容的影响。为啥加了这段代码就是用 GPU 渲染,并且晋升为 合成层
呢?以及 合成层
是什么?让咱们一起来看看吧。
2. 浏览器渲染流程
在探讨 合成层
之前咱们先简略理解一下浏览器渲染,浏览器常见的渲染引擎有 Webkit/Gecko 等,他们的次要渲染流程基本相同,这里次要讨论一下 WebKit 简化的渲染流程。
- 浏览器下载并解析 HTML。
- 解决
CSS
构建CSSOM
树,生成DOM
树。 DOM
与CSSOM
合并成一个Render
树。- 有了
Render Tree
,浏览器能够晓得各个节点的CSS
定义以及他们的从属关系,从而去计算出每个节点在屏幕中的地位,生成一个足够大的画布来包容所有元素。 - 依据浏览器提供各层的信息合成图层,显示到屏幕上。
本文的配角 合成层
就呈现在最初一步流程中,这些合成图层中一些非凡的图层被认为是合成层(Compositing Layers),咱们来具体看看它的由来吧。
3. 对于合成层
3.1 什么是合成层(Compositing Layer)
首先合成就是将页面的各个局部分成多个层、独自 光栅化
(浏览器依据文档的构造、每个元素的款式、页面的几何形态和绘制程序转换为屏幕上的像素的过程)它们并在合成器线程中合成为一个页面的技术。
如何去察看页面的图层构造呢,您须要在 Chrome
开发工具中关上自定义菜单,而后在 More tools 中抉择 Layers 选项。
这样你就能够察看页面的图层构造了,具体能够看 demo。
一般来说,领有一些特定属性的渲染层,会被浏览器主动晋升为合成层。合成层领有独自的图层(GraphicsLayer),和其余图层之间无不影响。而其它不是合成层的渲染层,则和第一个领有图层的父层共用一个,也就是一般文档流中的内容,咱们看一些常见的晋升为合成层的属性。
- 设置
transform: translateZ(0)
,留神它必须是translateZ
,因为它应用 GPU 来计算perspective distortion
(透视失真)。perspective
在 3D 设计中是一个重要的属性,有趣味的同学能够看这份材料理解一下。如果你应用translateX
或translateY
,元素将会被绘制在一般文档流中 demo。 - backface-visibility: hidden 指定当元素反面朝向观察者时是否可见 demo。
will-change
该属性通知浏览器该元素会有哪些变动,这样浏览器能够提前做好对应的优化筹备工作。当该属性的值为 opacity、transform、top、left、bottom、right 时 demo。video
、canvas
、iframe
等元素。
3.2 对于隐式合成
隐式合成就是特定场景下,存在会被默认晋升为合成层的状况。具体咱们能够再看一下之前图层构造的 demo,只有咱们把 B 和 C 的 z-index
替换一下你就会发现 B 被隐式的晋升为合成层了。
只是 z-index
导致的么如果咱们再调整一些 B 的地位,保障 B 和 C、D 没有交加,那么你会发现这次 B 并没有被隐式晋升为合成层了。
所以援用 CSS GPU Animation 中对于隐式合成的形容那就是:
This is called implicit compositing: One or more non-composited elements that should appear above a composited one in the stacking order are promoted to composite layers — i.e. painted to separate images that are then sent to the GPU.
一个或多个非合成元素应呈现在重叠程序上的合成元素之上,会被晋升为合成层。
3.3 层压缩与层爆炸
按咱们刚刚说的例子,如果在重叠程序底部有一个合成元素,那是不是会导致大量重叠程序上的元素被晋升为合成层?其实大部分状况下,咱们在开发过程中并不会去关注层合成的问题,那么咱们刚刚说的状况就会有产生的可能性。当这些不合乎预期的合成层达到一定量级时,就会产生 层爆炸
,这会导致你的页面占用大量的内存资源,带来一些无奈预期的状况。例如当 WKWebView
(WKWebView
是多过程组件,这意味着会从 APP 内存中拆散内存到独自的过程中)的内存超过零碎调配给它的内存的时候,浏览器就会解体白屏,然而 APP 不会 crash
。这是咱们不想看到的状况,面对这个问题浏览器也有绝对应的一些解决方案,如果多个渲染层同一个合成层重叠时,这些渲染层会被压缩到一个图层中,以避免因为重叠起因导致呈现的 层爆炸
。
咱们来看上面这段代码
B、C、D 原本都应该被晋升为合成层,然而因为产生层压缩,它们会渲染在一个图层外面。
近几年浏览器在这块的优化做的越来越好,比方咱们来看一个 CSS3 硬件加速也有坑文章中提供的一个乏味的 demo。页面中蕴含了一个 h1 题目,它对 transform
利用了 animation
动画,所以被晋升为合成层。因为 animation transform
的特殊性(动静交叠不确定),隐式合成在不须要交叠的状况下也能产生,就导致了页面中所有 z-index
高于它的节点所对应的渲染层全副晋升为合成层,最终让这个页面整整产生了几千个合成层。而后当我在本人电脑上测试这个例子的时候忽然发现几千个合成层隐没了,页面分外晦涩。Why?我的浏览器版本是 Chrome 96,我找了一下谷歌的历史包,最终测试发现这个问题在 Chrome 94 Releases 版本被优化了。
谷歌浏览器 93 Releases 版本
谷歌浏览器 96 Releases 版本
翻看了一下 Chrome 94
的更新日志,其中提到一条修复内容:
1238944 Medium CVE-2021-37966 : Inappropriate implementation in Compositing. Reported by Mohit Raj (shadow2639) on 2021-08-11
修复合成中不正确景象
因为对应 issues 没有权限拜访,有趣味的同学能够深究一下。层压缩
的存在并不代表咱们能够胡作非为的去晋升合成层,特地在一些对于渲染速度要求高的页面,或者自身加载速度慢的页面,咱们就应该关注一下页面的层级构造,简化绘制的复杂度,进步页面的性能。
4. 合成层的利弊
渲染层的提醒带来的益处:
- 开启硬件加速,合成层的位图会交由
GPU
合成,相比CPU
解决要快。 - 合成层产生
repaint
的时候,不会影响其余图层。 - 对于
transform
和opacity
成果,不会触发layout
和paint
。
当然合成层也存在一些问题:
- 如果咱们把所有渲染工作都交给
GPU
,在现有的优化下,它会导致渲染内存占用比大幅度晋升,反而呈现负面的成果。 - 另外隐式合成容易产生大量咱们意料之外的合成层,过大的内存占用,会让页面变的卡顿,性能优化事与愿违。
5. 总结
5.1 应用 transform 和 opacity 来实现动画
在咱们日常开发中常常会实现一些动画,有时候咱们可能会抉择扭转 top/left 去实现,那么这个节点的渲染会产生在一般文档流中。而应用 transform
和 opacity
实现动画可能让节点被搁置到一个独立合成层中进行渲染绘制,动画不会影响其余图层,并且 GPU 渲染相比 CPU 可能更快,这会让你的动画变的更加晦涩,咱们来看看他们的区别。
通过 left
来实现动画:
通过 transform
来实现动画:
能够看到通过 transform
来实现动画,页面的 fps
可能稳固在 60 左右,而通过 left
来实现存在稳定,fps
大略稳固在 30 左右,这会影响你的用户体验指标。
注:查看帧率的界面唤醒办法
如果你无奈确定应用这个属性是否正当,在你将任何 CSS 属性用于实现动画之前,你能够在 csstriggers 上查看该属性对渲染管道的影响。
5.2 审慎应用 will-change
我认为除非你的元素的真的存在某个属性马上会发生变化,例如 transform
,你能够应用 will-change: transform
告知浏览器,依据您打算更改的元素,浏览器可能能够预先安排,元素的扭转和渲染速度都会变得更快。可是这些属性可能会给你带来一些副作用,咱们来看一个 demo。
任何带有 position: fixed
或者 position: absolute
的子元素将会绝对于设置了 will-change: transform
的元素进行绝对定位。所以在你应用的时候须要确保这种意料之外 containing block
不会对你造成影响。除此之外浏览器用来为 will-change
属性做的更进一步的优化经常会消耗更多的资源,如果你将它施加在过多属性上显然是一个节约,更甚者十分适度的应用可能会造成页面相应速度的变慢或者间接解体。
5.3 减小合成层绘制区域
合成层的绘制区域大小,很大水平上影响了它的内存占用,咱们来上面这个例子:
能够看到 A
的尺寸是 B
的 5 倍,咱们通过 transform: scale(5)
放大 B
到 200 × 200 像素,然而它们内存占用上却相差了 25
倍之多。在用户看不到任何区别的前提下,你可能节俭大量的内存。当然这个例子只实用于这种纯色的场景,咱们须要看到的是绘制区域对于内存占有的影响。
相干材料
- CSS GPU Animation: Doing It Right
- Stick to Compositor-Only Properties and Manage Layer Count
- Eliminate content repaints with the new Layers panel in Chrome
- 无线性能优化:Composite
- Layers and how to force them
- GPU Accelerated Compositing in Chrome
- Layers and how to force them
- Inside look at modern web browser (part 3)
本文公布自 网易云音乐大前端团队,文章未经受权禁止任何模式的转载。咱们长年招收前端、iOS、Android,如果你筹备换工作,又恰好喜爱云音乐,那就退出咱们 grp.music-fe (at) corp.netease.com!