前端性能-合成层

本文深入探讨了前端页面渲染性能,重点关注Chrome浏览器中的渲染层合并(Composite)和硬件加速。渲染层由RenderLayers和GraphicsLayers组成,满足特定条件的元素会被提升为合成层,利用GPU进行高效合成。通过使用transform和opacity实现动画、减少不必要的重绘和合成层爆炸,可以优化页面性能。同时,要注意内存占用和过度使用合成层可能导致的问题。性能优化策略包括使用will-change属性提升元素为合成层,减少绘制区域,以及谨慎使用硬件加速属性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

页面性能是前端开发特别需要关注的重点,评判前端 Web 页面性能的指标有很多,页面的流畅度是其中比较重要一点,有时候我们发现我们的页面在使用了某些属性或者进行某种操作之后,浏览器的渲染的流畅性出现问题,一般情况是渲染线程出现了问题,出现大量的reflow和repaint,页面的渲染过程这里就不过多描述了,有兴趣可以翻看之前的恩张。这里就来跟大家探讨出现的原因和如何避免这种情况。

Composite:

Composite(渲染层合并):对页面中 DOM 元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上。对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。

注意:这里讨论的是 WebKit,描述的是 Chrome 的实现细节,而并非是 web 平台的功能,所以只是举个案例。

  • Chrome 拥有两套不同的渲染路径(rendering path):硬件加速路径和旧软件路径(older software path);
  • Chrome 中有不同类型的层: RenderLayer(负责 DOM 子树)和GraphicsLayer(负责 RenderLayer的子树),只有 GraphicsLayer 是作为纹理(texture)上传给GPU的。
  • 纹理——可以把它想象成一个从主存储器(例如 RAM)移动到图像存储器(例如 GPU 中的 VRAM)的位图图像(bitmapimage);
  • Chrome 使用纹理来从 GPU上获得大块的页面内容。通过将纹理应用到一个非常简单的矩形网格就能很容易匹配不同的位置(position)和变形(transformation)。这也就是3DCSS 的工作原理,它对于快速滚动也十分有效。

Chrome的层类型:

  • RenderLayers 渲染层,这是负责对应 DOM 子树。
  • GraphicsLayers 图形层,这是负责对应 RenderLayers子树。
渲染层合成:

在 DOM 树中每个节点都会对应一个渲染对象(RenderObject),当它们的渲染对象处于相同的坐标空间(z 轴空间)时,就会形成一个 RenderLayers,也就是渲染层。渲染层将保证页面元素以正确的顺序堆叠,这时候就会出现层合成(composite),从而正确处理透明元素和重叠元素的显示。

从浏览器的渲染过程中我们知道,页面 HTML 会被解析成 DOM 树,每个 HTML 元素对应了树结构上的一个 node 节点。而从 DOM 树转化到一个个的渲染层,并最终执行合并、绘制的过程,中间其实还存在一些过渡的数据结构,它们记录了 DOM 树到屏幕图形的转化原理,其本质也就是树结构到层结构的演化。

在这里插入图片描述

渲染对象(RenderObject)

一个 DOM 节点对应了一个渲染对象,渲染对象依然维持着 DOM 树的树形结构。一个渲染对象知道如何绘制一个 DOM 节点的内容,它通过向一个绘图上下文(GraphicsContext)发出必要的绘制调用来绘制 DOM 节点。

渲染层(RenderLayer)

这是浏览器渲染期间构建的第一个层模型,处于相同坐标空间(z 轴空间)的渲染对象,都将归并到同一个渲染层中,因此根据层叠上下文,不同坐标空间的的渲染对象将形成多个渲染层,以体现它们的层叠关系。所以,对于满足形成层叠上下文条件的渲染对象,浏览器会自动为其创建新的渲染层。能够导致浏览器为其创建新的渲染层的。

几类常见的情况:

  • 根元素 document;
  • 有明确的定位属性(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 应用动画;
  • overflow 不为 visible;

DOM 节点和渲染对象是一一对应的,满足以上条件的渲染对象就能拥有独立的渲染层。

图形层(GraphicsLayer)

GraphicsLayer 其实是一个负责生成最终准备呈现的内容图形的层模型,它拥有一个图形上下文(GraphicsContext),每个GraphicsLayer(合成层单独拥有的图层) 都有一个 GraphicsContext,GraphicsContext 会负责输出该层的位图,位图是存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后显示到屏幕上。

合成层(CompositingLayer)

RenderLayers 来保证页面元素以正确的顺序合成,这时候就会出现层合成(composite),从而正确处理透明元素和重叠元素的显示。满足某些特殊条件的渲染层,会被浏览器自动提升为合成层。合成层拥有单独的 GraphicsLayer,而其他不是合成层的渲染层,则和其第一个拥有 GraphicsLayer 的父层共用一个。

合成层创建标准:

  • 3D 或透视变换(perspective transform) CSS 属性;
  • 使用加速视频解码的 元素 拥有 3D;
  • (WebGL) 上下文或加速的 2D 上下文的 元素;
  • 混合插件(如 Flash);
  • 对自己的 opacity 做 CSS动画或使用一个动画变换的元素;
  • 拥有加速 CSS 过滤器的元素;
  • 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里);
  • 元素有一个z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染);

合成层的优点:

renderLayer提升为合成层就会有自己的绘图上下文,并且会开启硬件加速,有利于性能提升。

  • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快;
    提升到合成层后合成层的位图会交GPU处理仅仅只是合成的处理(把绘图上下文的位图输出进行组合)需要用到GPU,生成合成层的位图处理(绘图上下文的工作)是需要CPU。
  • 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层;
    repaint的时候可以只repaint本身,不影响其他层,但是paint之前还有style, layout,那就意味着即使合成层只是repaint了自己,但style和layout本身就很占用时间。
  • 对于 transform 和 opacity 效果,不会触发 layout 和 paint;

总结合成层的优势:一个元素开启硬件加速后会变成合成层,可以独立于普通文档流中,改动后可以避免整个页面重绘,有利于性能的提升。

性能优化方案:

  • 减少动画元素对其他元素的影响,从而减少paint,我们需要把动画效果中的元素提升为合成层。
    较好的方式will-change 设置为opacity、transform、top、left、bottom、right 可以将元素提升为合成层。
  • 使用 transform 或者 opacity 来实现动画效果, 这样只需要做合成层的合并。
  • 减少绘制区域 对于不需要重新绘制的区域应尽量避免绘制,以减少绘制区域。
注意:
  • 合成层占用内存的问题(每个合成层都占用额外的内存)。
  • 层爆炸
    由于某些原因可能导致产生大量不在预期内的合成层,虽然有浏览器的层压缩机制,但是也有很多无法进行压缩的情况,这就可能出现层爆炸的现象。(我们经常用translateZ(0)等属性来进行所谓的硬件加速,以提升性能,达到优化页面动态效果的目的。)
    最佳方案是打破 overlap 的条件,也就是说让其他元素不要和合成层元素重叠。简单直接的方式:使用3D硬件加速提升动画性能时,最好给元素增加一个z-index属性,人为干扰合成的排序,可以有效减少chrome创建不必要的合成层,提升渲染性能,移动端优化效果尤为明显。
  • 绘制的图层必须传输到 GPU,层的数量和大小达到一定量级后,可能会导致传输非常慢。

总结:

  • 动画使用 transform和opacity 实现
  • 适当使用合成层
  • 减少隐式合成
  • 减小合成层的尺寸
前端开发中,合成(Compositing Layers)是浏览器渲染引擎用于优化页面渲染性能的重要机制。某些特殊的渲染会被认为是合成,这些拥有独立的 `GraphicsLayer`,而普通渲染则与其父共享 `GraphicsLayer` [^2]。这种机制允许浏览器将部分绘制操作交由 GPU 处理,从而提高渲染效率。 ### 合成与内存占用 合成的物理尺寸会直接影响其内存占用。例如,`.bottom` 元素由于没有使用 `transform` 属性,导致其在 CPU 上进行缩放操作,内存占用达到了 39.1 KB;而 `.top` 元素通过 `transform: scale(...)` 进行放大操作,由于该操作发生在 GPU 上,因此内存占用仅为 400 B [^1]。这表明,合理利用 `transform` 可以显著降低合成的内存消耗。 为了进一步优化性能,可以采用以下策略: - **缩小物理尺寸后放大**:对于纯色图,可以通过设置较小的 `width` 和 `height`,再配合 `transform: scale(...)` 进行放大,这样可以在不牺牲视觉效果的前提下减少内存开销。 ```css .layer { width: 50px; height: 50px; transform: scale(2); } ``` ### 合成异常与崩溃问题 尽管合成有助于提升性能,但在实际应用过程中也可能引发异常或崩溃。常见的问题包括但不限于: - **GPU 内存溢出**:当页面中存在大量高分辨率图像或复杂的合成时,可能导致 GPU 内存不足,进而引起崩溃。 - **级过多**:过度使用合成会导致浏览器需要管理大量的 `GraphicsLayer`,增加 GPU 的负担,反而可能降低性能- **硬件加速失效**:某些情况下,浏览器可能无法正确启用硬件加速,导致原本应由 GPU 处理的操作回退到 CPU,影响渲染效率。 解决这些问题的方法通常包括: - **优化资源**:压缩图片大小、减少不必要的动画和透明度效果。 - **限制合成数量**:避免不必要的元素成为合成,仅对确实需要硬件加速的元素启用 `will-change` 或 `transform`。 - **监控性能**:使用 Chrome DevTools 等工具定期检查页面性能,识别并优化潜在瓶颈。 ### Vue 生命周期变化的影响 在 Vue 3 中,生命周期钩子发生了一些变化,如 `beforeCreate` 和 `created` 被 `setup()` 替代,`beforeDestroyed` 改为 `onBeforeUnmount`,`destroyed` 改为 `onUnmounted` [^3]。此外,新增了 `onRenderTracked` 和 `onRenderTriggered` 钩子函数,用于跟踪虚拟 DOM 的重新渲染过程。这些变化可能会影响组件的状态管理和合成的行为,尤其是在涉及动态创建和销毁元素的情况下。 ### JSClass 示例中的问题 在给定的 JavaScript 示例中,`jc.Render()` 方法创建了一个新的 `div` 元素,并将其添加到页面中;而 `jc.func()` 则尝试访问这个新创建的 `div` 元素的内容 [^4]。如果 `jc.func()` 在 `jc.Render()` 执行之前被调用,则可能会出现 `undefined` 错误,因为此时目标元素尚未被创建。确保方法调用顺序正确可以避免此类问题。 ```javascript var jc = new JSClass(); jc.Render(); // 创建 div 元素 jc.func(); // 正确输出 "division element" ``` ### 总结 合成是现代前端开发中不可忽视的一部分,它不仅影响着页面的渲染性能,还可能成为潜在的崩溃源。通过理解其工作机制以及如何优化,开发者可以更好地控制页面表现,提升用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值