听说有个能优化性能的属性 contain

- 2017年的老文,搬运存档用 -

有优化效果,但限制较多,X5 兼容 iOS 不行

属性值列表

  • layout:防止元素内 layout 改变影响元素外,也防止其他元素改变影响这个元素。
  • paint:元素的子元素必须在元素的 content-box 中,不能超出,否则显示不出。子元素发生的任何改变都不会影响到与该元素之外的其他元素;同样该元素之外的其他元素都不会影响到子元素
  • size:用子元素是撑不开这个元素的(声明都不给它的尺寸会一直是 0x0),必须声明尺寸,且子元素不能超出元素的范围,这个属性能够阻止子元素不断变大 -> 改变父元素尺寸 -> 影响更多节点 -> 发成大面积重排。本身提供不了太大性能优化,一般是和 layout 搭配使用。
  • style:有些 CSS 属性会影响不只宿主元素和其子元素,比如 counter。为了限制这样的属性影响到别的元素,让它的影响力限制在宿主元素和其子元素范围内。强行生成一棵 DOM 子树,变成像 shadow dom 那样的情况,外面的变量不会影响里面的;里面的也不会影响到外面。
  • strict:layout style paint size
  • content:layout style paint

适用场景

  1. 元素在屏幕外不可见时
  2. 第三方插件
  3. container queries

Use Case

注意:实验环境都是 macOS 10.12.6 Chrome stable 61。第三方插件和 cotainer queries 用得少,所以没研究。

case 1

If you build a off-screen navigation or similar, the browser paints the content completely although it is not visible on load. By setting contain: paint; the user agent can skip the paint off the off-screen element and therefore paint all the other content faster.

上文的意思是“如果构建一个屏幕外的导航栏(汉堡侧边栏),虽然看不到,但浏览器其实还会渲染那部分节点的。如果设置了 cotain: paint 那浏览器就不会去渲染屏幕外的东西,所以相对的屏幕内的内容就会被更快地渲染出来”。

为了验证这个理论,将 FMP (first meaningful page) 和首次可操作时间 (First interactive) 作为主要的衡量标准,毕竟对于用户来说最有感知的应该也是这两个特性了。推理过程是这样的:

第一个页面:侧边栏有一个高斯模糊的图片,并动态加上了 1000 个高斯模糊的纯色点;通过改变 left 值实现的移入移出视口。(实验原则就是,怎么慢怎么来,满足了自己的破坏欲= =)

点击右上角「按钮」,控制侧边栏的移动:

多次实验后结果差不多是下面这样(Chrome devTools 的 Audits 面板):

二者的区别是在侧边栏上有无 contain: paint需要特别注意的是,添加了 contain: paint 相当于给元素加了一个 position:relative; overflow: clip,所以子元素会相对这个父元素来定位了:

aside {
   position: absolute;
   z-index: 1;
   top: 0;
   bottom: 0;
   left: -101%;
   transition: left 500ms ease;

   width: 80vw;
   overflow: hidden; /* 为了和有 contain:paint 时的视觉效果一样*/

   background-color: #414529;
   background-image: url('http://qzonestyle.gtimg.cn/aoi/sola/20170707104843_SfolTLGT5s.jpg');
   background-size: contain;
   background-repeat: no-repeat;
   filter: blur(10px);

   contain: paint;
}

aside.open {
  left: 0;
}

结论:FMP 和 First Interactive 都有优化,其中 FMP 优化了40ms。

内存使用情况如下图,这是多次试验以后取了效果对比最明显的:

case 2

按道理来说我们不应该看 FMP 而是应该看渲染的节点个数,但是因为侧边栏本身就是在复合层上,不参与 layout 时被影响的节点统计,通过开发者工具也没有检测到明显的效果(从上图中的 'layout' 可以看出)。所以写了另一个 demo,用来验证 Paul Lewis 文章中的效果:

实验内容就是,一个从上到下排列的页面结构,在中间颜色为黄色的节点内不断插入新的子节点,将会触发重排:

多次试验后的结果如下:

二者区别在于黄色节点有没有 contain: layout size。另外 overflow: hidden 不会影响 layout root,但会影响 Nodes That Need Layout 这一栏。需要特别注意的是这两个属性值的使用场景,元素一定要有固定尺寸的。代码如下:

article {
  position: relative;
  z-index: 1;
  background-color: #FFE32A;

 /* no containment */
 /* height: 200px;
  * overflow: hidden;
  */

  /* has containment */
  height: 200px;
  contain: layout size;
  overflow: hidden;
}

结论是,在会引起重排的场景下,可以有效缩小重排的范围。

兼容性

感谢 TBS 的同事,TBS 线上版本已经支持,也就是大安卓市场可以用起来了!但是 iOS…

参考资料

  1. CSS containment
  2. container-queries-issue3
  3. W3C - containment
  4. MDN - contain
  5. CSS Containment in Chrome 52

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区