作者:ice_dk
原文链接:https://juejin.cn/post/7380268230798950440
🎈本篇文章仅以发表笔者学习性能优化过程中的一些思想输出!(回流重绘-CSS硬件加速篇)
css3 硬件加速?是不是听起来会觉得是一个很陌生的词🫨。css ?一个我写在编辑器上的东西,怎么能跟硬件挂上钩呢?这还能怎么优化,我平时写 CSS 的时候,直接框框一顿操作就足够了啊!且慢,还请你听我慢慢道来!
引言
相较于网络方面的优化,前端渲染的优化有时候会显得十分渺茫。随着浏览器跟硬件性能的增长,加上主流框架、工具的支持,已经大大减少了我们对于性能优化的工作量及需求。但对于前端渲染性能的提升,还是依然值得我们去学习的,笔者认为,这可能更多地体现在日常开发的思维和习惯中。我想一个合格的前端开发工程师,是绝不会放过任何一个能提高性能的小细节!
🤔那就开始跟着我的步伐一同学习吧,大家!
回流与重绘
🤗主要照顾到有的宝宝们可能不太了解这方面的知识,所以就特意开了一个标题来讲,即使你还是个小萌新,也能跟着一起看看!(当然,这里也只是带大家入个门!)
想知道什么是回流与重绘,我们可能得先回顾一下,当我们输入 URL,按下回车,浏览器是如何渲染一个页面的?
浏览器是如何渲染一个页面的
当浏览器的网络线程收到HTML
文档时。在事件循环机制的作用下,渲染主线程取出消息队列中的渲染任务,开启渲染流程。
其渲染的流程如下:
HTML
解析、样式计算、布局、分层、生成绘制指令、分块、光栅化、绘制
每个阶段都有明确的输入输出,上一个阶段的输出会成为下一个阶段的输入。
HTML解析:浏览器会将获取到的 HTML 代码解析成1个 DOM 树,该树以 document 对象作为根节点,以 HTML 中的每个 tag 作为 DOM 树中的1个节点。DOM 树里包含了所有的 HTML 标签,包括 display:none
隐藏的标签,还有用 JS 动态添加的元素,由图所示:
TIP:由于 HTML 文件会引入外部的 CSS、JS文件,为提高效率,浏览器在开始解析前会启动一个预解析的线程,率先下载外部的这些资源
(1)对于 CSS:如果主线程解析到 link 位置,此时外部的 css 文件还没有下载解析好,主线程不会等待,继续解析后续的HTML。这是因为下载和解析 CSS 的工作是在预解析线程中进行的。这就是 CSS 不会阻塞 HTML 解析的根本原因。
CSS 的计算主要是层叠,继承。最终会生成 CSSOM 树,浏览器的默认样式、内部样式、外部样式、行内样式均会包含在 CSSOM 树中
(2)而对于 JS:如果主线程解析到 script位置,会停止解析 HTML,转而等待 JS 文件下载好,并将全局代码解析执行完成后,才能继续解析HTML。预解析线程可以分担一点下载 JS 的任务。JS 代码的执行过程可能会修改当前的DOM树,所以DOM树的生成必须暂停。这就是 JS 会阻塞 HTML 解析的根本原因
样式计算:主线程会遍历得到的DOM树,依次为树中的每个节点计算出它最终的样式。在这个过程中可能会涉及值的变化:比如 em 会变成 px,black 变 rgb(0,0,0)等。 布局:接着是组合构建 render tree(也可以说是布局树),render tree 类似于 DOM tree。但区别很大,render tree 能识别样式,render tree 中每个 NODE 都有自己的 style,不一样的是 render tree 不包含隐藏的节点 (比如 display:none
的节点,还有 head 节点),因为这些节点不会用于呈现,而且不会影响呈现的节点,所以就不会包含到 render tree 中分层:主线程会使用一套复杂的策略对整个布局树中进行分层。
分层的好处在于,将来某一个层改变后,仅会对该层进行后续处理,从而提升效率。(你可以理解成,我们的网页有点类似于叠积木,有的积木在上方,有的积木在下面,只不过不一样的是空间上,此时整个大积木是从电脑里朝向电脑外进行摆放的)
具体的表现属性可能有:z-index, opacity, transform
绘制:主线程会为每个层单独产生绘制指令集,用于描述这一层的内容该如何画出来。
完成绘制后,渲染主线程的工作结束。剩下的工作就交给合成线程完成。
分块、光栅化、画:
(1)合成线程首先对每个图层进行分块,将其划分为更多的小区域。(你可以理解成,如果每个图层是大蛋糕,那分块的目的就是为了将它切成一片片小蛋糕🍰!)
(2)紧接着将块信息交给GPU进程,以极高的速度完成光栅化。
注:GPU 进程会开启多个线程来完成光珊化,并且优先处理靠近视口区域的块。
光栅化的结果,就是一块一块的位图(每个像素点的信息)
(3)合成线程拿到每层每块的位图之后,生成一个个指引信息,这些指引信息会识别出每个位图应该画到屏幕的哪个位置,以及会考虑到旋转,缩放等变形。
合成线程会把这些指引信息提交给 GPU 进程,由 GPU 进程产生系统调用,提交给 GPU 硬件,完成最终的屏幕成像
读到这里,我想告诉你呀!
其实浏览器上我们所能看见的元素。当它们的位置发生改变的时候,并不是流动的。而是先被擦除,再重新生成。这就像画画,当画上的某一个单位需要改变位置,我们无法直接把这个单位直接进行移动,只能先将其擦除,然后在指定的位置重新画一个。
这个过程就是,就是我上面写的那些东西!而不是简简单单地作移动噢!
概念剖析
理解了浏览器的渲染流程,接下来理解下面的内容就不算难啦!小问题😎
回流:当 render tree,即布局树中的一部分(或全部)因为元素的规模尺寸、布局、显示/隐藏等改变而需要重新构建。这个过程称作回流(reflow)。页面第一次加载的时候,至少发生一次回流。
(说人话:回流是指由于布局或者几何属性需要改变,引起布局树的重新计算!)
重绘:当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如 background-color,这个过程叫做重绘(repaint)。
(说人话:重绘是指当改动了可见样式后,需要重新根据分层信息计算绘制指令!)
所以说:回流必定发生重绘,重绘不一定引发回流。回流所需的成本比重绘高得多,改变深层次节点可能导致父节点一系列的回流。
在 chrome 中查看 回流(重排)
F12 打开控制台 -> DevTools -> Show console drawer -> Rendering -> 勾选 Paint flashing。
Fist:
Second:
影响的性能问题
😁想了想还是写一下哈哈,抱歉打扰到一些本来就懂的宝宝哩!还不懂的就跟我一起变聪明吧😉
上面我提到,当物体的样式、位置等发生改变的时候,并不是按照我们的意愿随随便便进行移动那么简单,浏览器它可能需要重新计算布局树,计算样式,然后生成相应的位图、信息来解决!那其实,这个计算肯定是会耗费很大的精力的,特别是对于回流的情况,它甚至还要我们计算布局树!这可是个大工程啊,若是浏览器频繁地进行这些操作,性能肯定会差很多!
那么,什么操作会进行回流重绘导致性能问题呢?(这里只提简单的,具体还需要大家去用工! )
添加或删除可见的DOM元素 元素的位置发生变化元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等) 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。 定位或者浮动 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
如何减少重绘和回流?
批量修改DOM 对于复杂动画效果,使用绝对定位让其脱离文档流 css3硬件加速(GPU加速) transform,opacity,filters这些动画不会引起回流重绘。
CSS3硬件加速
🎈好啦,开启我们的下半部分主题!
概念
CSS3 硬件加速
的别称有 GPU 加速
,是利用 GPU 进行渲染,减少 CPU 操作的一种优化方案。
上面我们提到,在浏览器的渲染过程中,有一步是光栅化,他会把块信息交给 GPU 进程,以此来获取一块一块位图,此时的样式交由 GPU 实现的,若是我们正确灵活地使用它,或许能取得很大的性能方面优化!
那什么是 GPU
呢?其实他是一种专门的图像处理器。而GPU 硬件加速
是指应用 GPU 的图形性能对浏览器中的一些图形操作交给 GPU 来完成
,因为 GPU 是专门为处理图形而设计,所以它在速度和能耗上更有效率。
比如:由于 GPU 中的 transform 等 CSS 属性不会触发重绘,所以能大大提高网页的性能!
案例
演示地址:position 与 transform 的对比[1]
我们可以看到,左边元素的动画通过 left/top 操作位置实现,右边元素的动画通过 transform: translate
实现。然后前面提到,你可以打开 chrome 的 “Paint flashing” 查看,绿色部分是正在重绘的内容!
我们其实可以很明了地看出来,左侧是一直处于重绘状态的
那么,为什么这里的 transfrom
不会导致重绘呢??
因为 transform 既不会影响布局也不会影响绘制指令,它影响的只是渲染流程的最后一个「画」阶段。
由于‘画’阶段在合成线程中,所以 transform 的变化几乎不会影响渲染主线程。反之,渲染主线程无论如何忙碌,也不会影响 transform 的变化。比如滚动条滚动。
这便于是利用了 GPU
实现的性能上的优化。那为什么这里又说是 CSS3硬件加速
呢?诶!说到点子上了,其实是因为 CSS 中的以下几个属性能触发硬件加速!
其原理是:CSS3中的一些属性,能够通过浏览器中缓慢的软件渲染引擎来实现、开启 GPU 加速!常见的属性如下:
transform
、opacity
、filter
、will-change
如果有一些元素不需要用到上述属性,但是需要触发硬件加速效果,可以使用一些小技巧来诱导浏览器开启硬件加速。
.ice_dk {
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-ms-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);
/**或者**/
transform: rotateZ(360deg);
transform: translate3d(0, 0, 0);
}
注意问题
我们其实能感受到,硬件加速给我们带来的便利,但与此同时,我们也不能忘记,它给我们带来的一些负面因素:
(1)过多地开启硬件加速可能会耗费较多的内存,因此什么时候开启硬件加速,给多少元素开启硬件加速,需要用测试结果说话。
(2)GPU 渲染会影响字体的抗锯齿效果。这是因为 GPU 和 CPU 具有不同的渲染机制,即使最终硬件加速停止了,文本还是会在动画期间显示得很模糊。
总结
总的来说,我们还是需要根据不同的应用场景,去选择性地、习惯性地去优化自己的代码,CSS3 硬件加速,本质上是利用 GPU 的一些特性去实现它的目的,更回流重绘、浏览器渲染的知识点有关,有优点也有缺点!更多的最佳实践,还是要看聪明的你呢!
欢迎长按图片添加 poetry 为好友
,我会第一时间和你分享前端行业趋势,学习途径,成长内容等等。2024年陪你一起度过!点击领取精心为您整理的前端面试资料
如果觉得这篇内容对你有帮助,我想请你帮我2小忙:
点赞和在看就是最大的支持❤️