网页渲染性能优化 —— 性能优化下

关注公众号 前端开发博客,回复“加群”

加入我们一起学习,天天进步

作者:晨风明悟

链接:https://zhuanlan.zhihu.com/p/39880024

第一篇:网页渲染性能优化 —— 渲染原理

第二篇:你需要知道的网页渲染性能优化方法(上)

这是第三篇,以下为正文

Composite 的优化

终于,我们到了像素管道的末尾。对于这一部分的优化策略,我们可以从为什么需要 Composited Layer(Graphics Layer)来入手。这个问题我们在构建 Graphics Layer Tree 的时候,已经说明过,现在简单回顾一下:

  1. 避免不必要的重绘。

  2. 利用硬件加速高效实现某些 UI 特性。

根据 Composited Layer 的这两个特点,可以总结出以下几点优化措施。

使用 transform 和 opacity 属性来实现动画

上文我们说过像素管道的 Layout 和 Paint 部分是可以略过,只进行 Composite 的。实现这种渲染方式的方法很简单,就是使用只会触发 Composite 的 CSS 属性;目前,满足这个条件的 CSS 属性,只有 transform 和 opacity。

使用 transform 和 opacity 需要注意的是:元素必须是 Composited Layer;如果不是,Paint 还是会照常触发(Layout 要看情况,一般 transform 会触发)。来看一个例子:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <style>
    .div {
      width: 100px;
      height: 100px;
      background-color: #f00;
      /* will-change: transform; */
    }
  </style>
  <title>性能优化</title>
</head>

<body>
  <div class="div"></div>
  <script>
    const div = document.querySelector(".div");
    const run = () => {
      div.style.transform = "translate(0, 100px)";
    };
    setTimeout(run, 2000);
  </script>
</body>
</html>

我们将使用 transform 来向下位移,开始我们先不把 div 节点提升为 Composited Layer;通过下图可以看到:还是会触发 Layout 和 Paint 的。

这时,把 div 节点提升为 Composited Layer,我们发现 Layout 和 Paint 已经被略过了,符合我们的预期。

减少绘制的区域

如果不能避免绘制,我们就应该尽可能减少需要重绘的区域。例如,页面顶部有一块固定区域,当页面某个其他区域需要重绘的时候,很可能整块屏幕都要重绘,这时,固定区域也会被波及到。像这种情况,我们就可以把需要重绘或者受到影响的区域提升为 Composited Layer,避免不必要的绘制。

提升成 Composited Layer 的最佳方式是使用 CSS 的 will-change 属性,它的详细说明可以查看 MDN 的文档。

.element {
  will-change: transform;
}

对于不支持的浏览器,最简单的 hack 方法,莫过于使用 3D 变形来提升为 Composited Layer 了。

.element {
  transform: translateZ(0);
}

根据上文所讲的例子,我们尝试使用 will-change 属性来让固定区域避免重绘。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <style>
    .div {
      width: 100px;
      height: 100px;
      background-color: #f00;
    }
    .header {
      position: fixed;
      z-index: 9999;
      width: 100%;
      height: 50px;
      background-color: #ff0;
      /* will-change: transform; */
    }
  </style>
  <title>性能优化</title>
</head>

<body>
  <header class="header">固定区域</header>
  <div class="div">变动区域</div>
  <script>
    const div = document.querySelector(".div");
    const run = () => {
      div.style.opacity = 0.5;
    };
    setTimeout(run, 2000);
  </script>
</body>
</html>

首先,我们来看下没有经过优化的情况;顺带说明查看浏览器一帧绘制详情的过程。

  1. 打开控制台的 Performance 界面。

  2. 点击设置(标记 1),开启绘制分析仪(标记 2)。

  3. 启动 Record(标记 3),获取到想要的信息后,点击 Stop(标记 4), 停止 Record。

  4. 点击这一帧的 Paint(标记 5)查看绘制详情。

  5. 切换到 Paint Profiler 选项卡(标记 6),查看绘制的步骤。

通过上面的图片(标记 7 和标记 8)可以看到,固定区域的确被波及到,并且触发重绘了。我们再对比使用 will-change 属性优化过的情况,发现固定区域没有触发重绘。

并且,我们也可以通过一帧(标记 1)的布局详情(标记 2),查看固定区域(标记 3)是不是提升成 Composited Layer(标记 4),才避免的不必要绘制。

合理管理 Composited Layer

提升成 Composited Layer 的确会优化性能;但是,要知道创建一个新的 Composited Layer 必须要额外的内存和管理,这是非常昂贵的代价。所以,在内存资源有限的设备上,Composited Layer 带来的性能提升,很可能远远抵不上创建多个 Composited Layer 的代价。同时,由于每一个 Composited Layer 的位图都需要上传到 GPU;所以,不免需要考虑 CPU 和 GPU 之间的带宽以及用多大内存处理 GPU 纹理的问题。

我们通过 1000 个 div 节点,来对比普通图层与提升成 Composited Layer 之后的内存使用情况。可以发现差距还是比较明显的。

最小化提升

通过上文的说明,我们知道 Composited Layer 并不是越多越好。尤其是,千万不要通过下面的代码提升页面的所有元素,这样的资源消耗将是异常恐怖的。

* {
  /* or transform: translateZ(0) */
  will-change: transform;
}

最小化提升,就是要尽量降低页面 Composited Layer 的数量。为了做到这一点,我们可以不把像 will-change 这样能够提升节点为 Composited Layer 的属性写在默认状态中。至于这样做的原因,我会在下面讲解。

看这个例子,我们先把 will-change 属性写在默认状态里;然后,再对比去掉这个属性后渲染的情况。

.box {
  width: 100ox;
  height: 100px;
  background-color: #f00;
  will-change: transform;
  transition: transform 0.3s;
}
.box:hover {
  transform: scale(1.5);
}

使用 will-change 属性提升的 Composited Layer:

普通图层:

我们发现区别仅在于,动画的开始和结束,会触发重绘;而动画运行的时候,删除或使用 will-change 是没有任何分别的。

我们在构建 Graphics Layer Tree 的时候讲到过这样一条理由:

对 opacity、transform、fliter、backdropfilter 应用了 animation 或者 transition(需要是 active 的 animation 或者 transition,当 animation 或者 transition 效果未开始或结束后,提升的 Composited Layer 会恢复成普通图层)。

这条理由赐予了我们动态提升 Composited Layer 的权利;因此我们应该多利用这一点,来减少不必要的 Composited Layer 的数量。

防止层爆炸

我们在 Graphics Layer Tree 中介绍过层爆炸,它指的是由于重叠而导致的大量额外 Composited Layer 的问题。浏览器的层压缩可以在很大程度上解决这个问题,但是,有很多特殊的情况,会导致 Composited Layer 无法被压缩;这就很可能产生一些不在我们预期中的 Composited Layer,也就是说还是会出现大量额外的 Composited Layer。

在层压缩这一节,我们已经给出了使用层压缩优化的例子,这里就不再重复了。下面再通过解决一个无法被层压缩的例子,来更为深入的了解如何防止层爆炸。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <style>
    .animating {
      width: 300px;
      height: 30px;
      line-height: 30px;
      background-color: #ff0;
      will-change: transform;
      transition: transform 3s;
    }

    .animating:hover {
      transform: translateX(100px);
    }

    ul {
      padding: 0;
      border: 1px solid #000;
    }

    .box {
      position: relative;
      display: block;
      width: auto;
      background-color: #00f;
      color: #fff;
      margin: 5px;
      overflow: hidden;
    }

    .inner {
      position: relative;
      margin: 5px;
    }
  </style>
  <title>性能优化</title>
</head>

<body>
  <div class="animating">动画</div>
  <ul>
    <li class="box">
      <p class="inner">提升成合成层</p>
    </li>
    <li class="box">
      <p class="inner">提升成合成层</p>
    </li>
    <li class="box">
      <p class="inner">提升成合成层</p>
    </li>
    <li class="box">
      <p class="inner">提升成合成层</p>
    </li>
    <li class="box">
      <p class="inner">提升成合成层</p>
    </li>
  </ul>
</body>
</html>

当我们的鼠标移入 .animating 元素的时候,通过查看 Layers 面板,可以很清晰的看到出现的大量 Composited Layer。

这个例子虽然表面上看起来没有发生重叠;但是,因为在运行动画的时候,很可能与其他元素造成重叠,所以 .animating 元素会假设兄弟元素在一个 Composited Layer 之上。这时,又因为 .box 元素设置了 overflow: hidden; 导致自己与 .animating 元素有了不同的裁剪容器(Clipping Container),所以就出现了层爆炸的现象。

解决这个问题的办法也很简单,就是让 .animating 元素的 z-index 比其他兄弟元素高。因为 Composited Layer 在普通元素之上,所以也就没有必要提升普通元素,修正渲染顺序了。这里我在顺便多说一句,默认情况下 Composited Layer 渲染顺序的优先级是比普通元素高的;但是在普通元素设置 position: relative; 之后,因为层叠上下文,并且在文档流后面的原因,所以会比 Composited Layer 的优先级高。

.animating {
  position: relative;
  z-index: 1;
  ...
}

当然,如果兄弟元素一定要覆盖在 Composited Layer 之上,那我们也可以把 overflow: hidden; 或者 position: relative; 去掉,来优化 Composited Layer 创建的数量或者直接就不创建 Composited Layer。

参考资料

  1. 无线性能优化:Composite

  2. 坚持仅合成器的属性和管理层计数

  3. 简化绘制的复杂度、减小绘制区域

  4. CSS Animation性能优化

  5. 使用CSS3 will-change提高页面滚动、动画等渲染性能

  6. CSS3硬件加速也有坑

  7. 深入理解CSS中的层叠上下文和层叠顺序

总结

本文首先讲了渲染需要构建的一些树,然后通过这些树与像管道各部分的紧密联系,整理了一些优化措施。例如,我们对合成所进行的优化措施,就是通过 Graphics Layer Tree 来入手的。

优化也不能盲目去做,例如,提升普通图层为 Composite Layer 来说,使用不当,反而会造成非常严重的内存消耗。应当善加利用 Google 浏览器的调试控制台,帮助我们更加详尽的了解网页各方面的情况;从而有针对性的优化网页。

文章参考了很多资料,这些资料都在每一节的末尾给出。它们具有非常大的价值,有一些细节,本文可能并没有整理,可以通过查看它们来更为深入的了解。

相关文章

  1.  网页渲染性能优化 —— 渲染原理

  2.  你需要知道的网页渲染性能优化方法(上)

  3.  前端都该懂的浏览器工作原理,你懂了吗?

推荐

关注公众号:前端开发博客,后台回复以下关键字:

  1. 回复「1024」领取前端进阶资料

  2. 回复「电子书」领取海量面试和JS资料

  3. 回复「资料」领取前端群分享及培训机构的资料

  4. 回复「Vue」获取 Vue 精选文章

  5. 回复「面试」获取 面试 精选文章

  6. 回复「JS」获取 JavaScript 精选文

  7. 回复「CSS」获取 CSS 精选文章

“在看”吗?在看就点一下吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值