引言:此篇文章是我们 Code Optimization serie 系列的一部分,在这里我们将看到如何优化代码来提高性能,并提升编程能力。
此篇文章我们将为了通过 css 提升网站性能,总结出一些技巧。在我们讲解技巧之前,让我们简单的看下网页的渲染流程,以便更好的通过 CSS 解决网页的性能问题。
在 DOM 创建完之后,浏览器会执行以下的操作:
1. Recalculate Style (and render tree creation). 浏览器会计算应用到 dom 元素上的 style 样式。在创建 render tree 过程中,会抛弃 display:none 样式的元素以及一些伪元素,因为这些元素本身就不会被渲染。
2. Layout (aka Reflow). 根据上一步骤计算得到的 style,浏览器会为页面中的每一个元素计算位置以及形状信息(比如宽高).
3. Repaint. 一旦 Layout 完成,像素就会被绘制在屏幕上.
4. Composite Layers. 在 repainting 期间,Composite Layers 会自动的在不同层执行,并且这些层最终会组合在一起。可以通过以下方法查看不同的层。
接下来我们会在前三个阶段优化 css 代码来提升性能
1. Reduce Style Calcucalations
像之前提及的那样,style calculation 阶段,浏览器会计算应用在每一个元素上的样式。所以浏览器首先会在 CSS 找到指向该 DOM 元素的所有选择器。然后会遍历这些选择器,并找到真正应用到元素上的样式。
为了避免 style calculation 耗时,需要减小复杂的和深层嵌套的选择器,这样浏览器就能够更快的找到元素真正应用的样式。
另外还有一些别的方法,比如尽可能地减少 style rule 的数量,移除不用的 css,避免多余的以及重写的样式。这样浏览器在 style calculation 中就没必要一遍又一遍的遍历相同的 style rule。
为了降低 style calculation 耗时,减小 DOM 数的规模也能达到和减少 style rules 一样的效果
2. Reduce Reflows
一个元素的回流(reflow)或者布局改变是一件非常耗时的过程,特别当子元素数量特别多的时候,这问题将变得更大。
在 google,我们尝试通过不同的方法去优化页面性能,当我们在我们 UI 上增加 feature 时, reflow 是一个关键的考虑因素。 --Google Developers
元素的布局变化会导致回流,比如宽高等几何属性变化、字体大小变化、增删元素的 class 属性,window resizing,激活 :hover 状态, DOM 树变化等。
和 style calculation 一样,避免复杂的 css 选择器和较深的 DOM 树结构,同样能够减少回流开销。(因为这能够避免过度的回流层级,因为一个元素的回流会导致起子元素的回流操作, 参考 sites.google.com/site/getsni…)
如果你一定要改变页面内组件的样式,最好将样式应用在尽可能低层次的元素上,这样能避免掉不必要的回流操作。
如果你需要给布局的变化增加动画操作,可以给需要回流的组件赋予 position: absolute 样式,因为绝对定位的元素回流不会影响其他元素的回流。
小结:
- 布局变化时,应用的目标元素在 DOM 树中的层次尽可能低。
- 给布局变化较动画时,可以考虑增加绝对定位
- 尽可能避免布局属性的动画
3. Reduce Repaints
重绘是指将像素绘制在屏幕上,这和回流(reflow)一样也是一个耗时的操作。回流会在以下场景下被触发:回流,页面滚动,color、visibility 和 opacity 等属性的改变。
有些像 box-shadow 等操作会比较耗时,尤其当你将其应用到 transition 中,因为浏览器会在每一帧中都会去绘制。
为了避免频繁的耗时,尽可能少用像 shadow 这种会劣化重耗时的属性。
如果你相对那些会造成重绘的属性增加动画时,最好是能够将其元素放入独立的 layer(比如 compositor layer),这样就不会影响页面的其他部分并且可以利用硬件加速的优势。在硬件加速中,GPU 会承担动画执行的任务,从而减小 CPU 额外的开销。
简而言之,硬件加速意味着 GPU 能够在浏览器渲染过程中去协助做一些比较耗时的工作,而不是全部抛给 CPU 去计算。 -- Sara Soueidan
在一些浏览器中,opacity(value 值小于1)和 transform (不为 none 的值)会自动的被提升到一个新的独立渲染层,并会将硬件加速应用到 animation 和 transition 中。因此更倾向于将这些属性应用到动画属性当中。
在动画过程中,为了能强行的将元素提升到新的渲染层并执行硬件加速,主要有以下两种方式可以达到:
- 增加 transform: translate3d(0,0,0); 样式,这样式能够让浏览器使用硬件加速去渲染animation 和 transition。
- 增加 will-change 属性,dev.opera.com/articles/cs…
小结:
- 避免应用重绘耗时大的样式
- 对于复杂的 animation 和 transition,尝试使用层级提升和硬件加速
总结
(1) 直到现在,我们还没提及 CSS 文件大小的减小。上述提到的 css rule(和 DOM 元素) 的减少对性能有一定的提升,它能够在计算样式时更好的减少浏览器的工作量。由于代码的减少,写出更好的选择器并且删除未使用的样式,文件的大小将会自动减小。
(2) 建议不要使用太多的style样式应用,而是通过class代替,如下图所示:
(3) 尽量避免重排(layout), 比如通过js获取或修改一些会导致重排的css属性,如下图所示:
导致重排的属性可参考:juejin.cn/post/698319…