内容来源:2018 年 6 月 30 日,饿了么前端主管向勇在“饿了么技术沙龙・第27弹 【前端专场】”进行《h5渲染性能一瞥》演讲分享。IT 大咖说(微信id:itdakashuo)作为独家视频合作方,经主办方和讲者审阅授权发布。
阅读字数:2488 | 7分钟阅读
获取嘉宾演讲视频及PPT:http://suo.im/4SkvOx
摘要
前端性能按照类型来分主要分为加载性能和渲染性能。加载性能对于首屏的展示及其重要,而渲染性能对于页面加载完成后的交互体验极其重要。但目前绝大部分同学在提到前端性能的优化时都会默认等同于对加载性能的优化,而忽略了渲染性能。本次议题就从几个比较常见的角度聊聊开发中会无意识碰到的渲染性能问题。
H5 VS Native
在将H5和native进行对比的时候,我们通常能想到的一点就是“快”,H5相对于native开发和上线都会快一些,一般的活动页面和非关键页面更多的倾向于H5来开发。
当然H5也有相应的缺点,它的加载和操作会慢一些,抽象来看就是性能问题。加载慢对应的是加载性能,操作慢对应的是渲染性能。加载性能可被定义为页面首次加载时候的性能,这方面的优化方案主要有静态资源压缩、懒加载、雪碧图、CDN、server push等。
渲染性能可被定义为页面进行操作/交互时候的性能,对于这方面的优化可能并不太容易想到。我在面试的时候也经常会问面试者关于性能优化的问题,不过大部分提到的优化方案都是关于加载性能,少部分会提到GPU加速,相对来说这部分同学的CSS技能会高一些。
渲染
一般一次渲染都会经过JavaScript > style > layout > paint > compoasite这样的过程。在做动画的时候可以进行优化,将layout和paint省略掉,这其实是将做动画的元素提升为一个单独的层。
渲染性能的优化可以针对渲染过程中的每一步来做,下面列出了google开发者论坛中提到的具体优化措施。
优化JS执行(JS)
缩小样式计算的范围并降低其复杂性(style)
避免大型、复杂的布局和布局抖动(layout、paint)
使用输入处理程序去除抖动(layout、paint)
坚持仅合成器的属性和管理层技术(composite)
简化绘制的复制度、减小绘制区域(paint、composite)
层的优化
上面加粗的两个优化手段可以总结为层的优化,也就是开启GPU加速。这里我们先假设一个场景,在一个页面中存在两个水平排列的元素1、2,他们分别位于左右两端,我们要做的是将1移动到2的位置。对此最简单的方案是设置position并改变right或left的值。第二种方式是使用transform:translateX,并加上 will-change: transform/transform: translate(0)将它提升为单独的层,这种方案的好处在于启用了GPU加速。
这样看来只要应用GPU加速就能很好的解决动画优化问题,但是实际应用中的页面往往要比上面所描述的场景复杂的多。就拿饿了么的H5页面来说,它除了有轮播外还涉及到页面滚动、点击展开,返回顶部等。当开启layer borders查看时会发现滑动的过程中如果轮播图正在播放整个页面就会创建很多不必要的层。另外开启Paint flashing查看重绘情况时也会发现每次轮播图播放都会导致整个页面重绘。这种问题在低端手机上可能会造成闪屏,需要额外注意。解决方案其实前面也提到过就是要将做动画的元素提升为一个单独的层(合成层)。
之前说过动画的问题有两种解决方案,如果这两个方案结合在一起又会怎么样呢,也就是将position和will-change写在同一个元素上,这在实际写代码的过程中是很容易碰到的。由此引出了新的问题,浮动元素(渲染层)和合成层的关系。对此我个人做了下总结:若合成层的z-index值小于下方兄弟元素,且他们有重叠,则下方兄弟元素也会被提升为合成层。
上图是饿了么页面的简化场景,区域1是可滑动动画区,使用flex布局实现,区域2是店铺列表,区域3是店铺信息,这两个区域都添加了position:relative。
这种实现方式没有指定浮动层的z-index值,因此在区域1进行滑动的时候,下方的每个店铺列表都会被提升为单独的层。在为区域1设置position:relative和z-index:1(高于下层)之后下方的层就不会再被提升了(此时下层z-index未设置)。
层爆炸
注意图中标记区域,当点击展开/收起活动的小三角的时候会有一个旋转180度的交互效果,相信大家对此都很熟悉。这种效果实现起来也很简单,设置transition过渡属性就能完成。
在实际操作的时候查看层级会发现,每个商店列表都被提升成单独的层且有很多嵌套。造成此问题的原因和前面的案例类似,主要还是没有给拥有过渡动画效果的小三角元素添加z-index值,解决方案同样是为动画元素设置z-inde。
这一系列的问题涉及到一个概念:层压缩,即如果多个渲染层同一个合成层重叠时候,这些渲染层会被压缩到一个GraphicsLayer中。
另外如果元素有动画/过渡效果,可未指定层级顺序高于下方浮动层,此时会假定下方的浮动层在动画期间会受影响,从而无法被压缩。
减少绘制区域
一般我们编写页面的时候都会为头部和底部设置固定浮动,这涉及到减少绘制区域的优化策略。在没有设置浮动的情况下,每次页面滚动头部和底部就会被重新渲染,解决方案是设置浮动后将这些浮动的头部和底部提升为单独的层。