前言
上周我与阿里的宇果有一次技术的交流,然后对天猫H5站点做了一些浅层次的分析,后面点时间基本天天都会有联系,中途聊了一些技术细节、聊了双方团队在干什么,最后聊到了前端优化。因为我本身参与了几次携程H5站点的优化,在这方面有一些心得,但是与宇果交流的过程中发现我们在优化的时候忽略了一些细节。
携程做优化的时候整个重心基本放到了尺寸的缩减,和宇果的交流过程中他提出了渲染优化,其实渲染优化无非是减少回流,对于减少回流我们也有一些概念,我一直认为这个事情应该业务开发关注而不是框架关注(事实上框架也无法关注),所以对一些BUG采取了表现层面的解决,却对真相视而不见的做法,现在想来真的有点无知,这里便以一个原来的渲染BUG为切入点,将最近与宇果的交流所得整理下。
宇果博客:http://www.ghugo.com/
回流与重绘
首先,我们再次复习一下回流与重绘的知识,浏览器会解析三个东西:HTML、Javascript、CSS。
浏览器首先会根据HTML生成DOM Tree,其次会根据CSS生成CSS Rule Tree,javascript可以通过DOM API与CSS API操作DOM Tree与CSS Rule Tree,从而引起页面变化。
浏览器解析结束会通过DOM Tree与CSS Rule Tree形成render tree,只有display不为none的元素才会形成render Tree,render Tree形成后浏览器会调用GUI绘制页面,在此之前做的一件事情便是layout或者说reflow。上面的描述简单而言可以分为以下流程:
l  生成DOM树
l  计算CSS样式
l  构建render tree
l  reflow,定位元素位置大小
l  绘制页面
在这个过程中,若是javascript动态改变DOM Tree便会引起reflow
页面中的元素改变,只要不影响尺寸,比如只是颜色改变只会引起repaint不会引起回流
否则,reflow不可避免,这个时候便需要重新计算形成render Tree
reflow分为局部回流与全局回流,会影响下面的,不会影响上面的元素
reflow耗用的系统资源较大,DOM Tree中受到影响的节点皆会reflow,然后影响其子节点最坏的情况是所有节点reflow,该问题引发的现象便是低性能的电脑风扇不停的转,手机变得很热,并且非常耗电,以下操作可能引起reflow
l  操作dom结构
l  动画
l  DOM样式修改
l  获取元素尺寸的API
android大屏手机渲染BUG
这个是我们一个tab组件,这个组件按道理说非常简单:
View Code
点击一下就让当前item获取class,然后文字样式会改变,下面一个横线也会动画滑动过去,但是这个东西在android大屏手机上却出了问题(比如note2),他会这样表现:
如图所示,class过去了,文字颜色也变了,但是下面对应的竖条却没有过去,我当时一看就知道是渲染的问题并且马上给出了一个解决方案:
View Code
这里设置下划线width的时候触发了其回流,而setTimeout会在主程序执行结束后才触发,于是BUG修复了,核心代码如下:
1 $(function () {
2   var wrapper = $('.cui-tab-mod');
3   wrapper.on('click', function (e) {
4     var el = $(e.target);
5     var toolBar = $('.cui-tab-scrollbar');
6     if (el.hasClass('cui-item')) {
7       wrapper.find('li').removeClass('cui-tab-current');
8       el.addClass('cui-tab-current');
9       //修复丢帧问题
10       setTimeout(function () {
11         toolBar.width('width', toolBar.width());
12       }, 0)
13     }
14   });
15 });
这个BUG解决后也没有再关注,时序纷飞,一年过去了却和宇果聊天过程中再次想起这个问题,才意识到自己忽略的是什么。
重新认识Timeline
我们常常有一个问题,如何知道页面哪里的渲染有问题,如何捕捉?
如果没有一个实际可用的工具的话,我们只能由外文或者博客获取这类知识,这个时候就和我解决上述问题差不多了,仅仅从皮毛上知道是渲染导致的问题,并且能使用回流解决问题,却是对渲染性能造成负担,所以如何捕捉渲染问题衡量渲染性能这个是第一位,好在chrome提供了Timeline:
以我们的页面为例,我们开始监控,这里稍微改变一下代码:
1 var wrapper = $('.cui-tab-mod');
2 wrapper.on('click', function (e) {
3   var el = $(e.target);
4   var toolBar = $('.cui-tab-scrollbar');
5   if (el.hasClass('cui-item')) {
6     wrapper.find('li').removeClass('cui-tab-current');
7     el.addClass('cui-tab-current');
8 //      setTimeout(function () {
9 //        toolBar.width('width', toolBar.width());
10 //      }, 0)
11   }
12 });
13 
14 function setIndex(i) {
15   var els = wrapper.find('li');
16   els.removeClass('cui-tab-current');
17   els.eq(i).addClass('cui-tab-current');
18 }
我首先点击开始监控,然后鼠标不在页面做任何操作,在控制台输入一句:
setIndex(1)
于是页面会发生改变,这个时候,我们得到的图是:
这张图其实最初我也是看热闹的,昨天根据宇果的指点,才稍微看出点门道了:
首先请看上图中的一个个透明框,每个透明框便是两次显示器刷新周期中等待的时间,其中有一个非常关键的单位叫做fps,这个60fps是个什么东西呢?
60fps与16ms
所谓60fps便是说屏幕1s会刷新60次,意思是大概16.5ms屏幕会刷新一次,在这一次刷新中浏览器会干很多事情,这里还是以图示为例,这里有2个前提:
① 我们鼠标并未操作
② 我们页面很单一
所以在我们执行setIndex前,浏览器根本没有任何消耗,每一次的刷新干了这几件事情:
刚刚整个过程一过经过了2.29秒,浏览器发生了这些事情:
从白条来看,浏览器处理整个动作大概花费320ms,这里出了4种颜色,这个也比较关键。
4种颜色
黄色是说明正在执行javascript脚本,从这里可以看出,js执行的速度是非常快的,这里包含了设值class、移除class,获取width等一系列操作,浏览器会等一次结束后才发生渲染。
紫色是说明正在回流,也就是我们传说中的渲染,这个是非常耗费性能的,我们这里具体跟进去看看:
接下来触发了css3过渡transition的动画,下面的滚动条开始了不停的移动:
可以看到,后续所有的运动只影响了一个元素,这个元素便是滚动条,这里为了验证刚刚所说,我们做两个事情:
① 使用click触发索引更改
② 为下划线添加子元素
这里有个非常不同的就是,我鼠标在界面操作了,不可避免的会引起一些move样式改变,所以有重绘有回流,都比较轻,比较关键的是,其中多了一个click操作(Event),但是下面动画时候影响的元素仍然只是一个,这个有点不明原因,我这里特别将transform改成了left,因为transform更加平滑,我想找出原因,却发现了另一个现象:
可以看出transformd导致的动画只会执行一次paint,便是最初那一次,而使用left的话却在不停的paint,这个是资源消耗的对比:
可以看出,使用left时候出现了锯齿状,这个时候我们可以得出一个结论,transform的动画确实比left节省性能。
至此我们对chrome的Timeline有了一个新的认识,但是我们这里的观察全部是在PC端发生的,而手机端是怎么样的呢?
移动端的渲染
这个图与上图形成了鲜明对比,PC端就只有很小的波动,手机端却引起了很多涟漪,导致这个问题的原因是硬件设施(这里是盗图):
PS:这里苦于没有Android设备,chrome电脑又暂时抽风不能真机调试,便去同事那里草草截图,哎......
丢帧的发生
如前所述,浏览器一次的刷新约16ms,以一次原子行为来说是这样的:
脚本运行时间+渲染时间+绘制时间<16ms
如果中间有一次的操作比如渲染的时间大于了16ms而导致浏览器没有来得及绘制,那么那次的操作便失效了,这个就是所谓的丢帧。
我们在note2上运行程序时会导致丢帧,应该就是其中一次超出了时限,因为class变化会引起样式重新计算,但是只是文字变化的话只是会引起重绘,所以主要问题发生在transition动画。
而我们的解决方案是setTimeout,这个造成的影响是:
我们发现了,就算没有动画,重新计算样式那里依旧是主要导致的回流的因素,是他挤掉了后续的操作吗?他重新计算样式中的因素有很多,我怀疑的目光首先放到了这段css:
.cui-tab-mod li:nth-of-type(1).cui-tab-current ~ .cui-tab-scrollbar { -webkit-transform: translate3d(0,0,0); 
-moz-transform: translate3d(0,0,0); -ms-transform: translate3d(0,0,0); transform: translate3d(0,0,0); }
测试下来,似乎与他相关:
1 <!DOCTYPE html>
2 <html xmlns="http://www.w3.org/1999/xhtml">
3 <head>
4   <meta name="viewport" content="width=320.1,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,minimal-ui">
5   <title></title>
6   <style type="text/css">
7     body { -webkit-text-size-adjust: none; -webkit-tap-highlight-color: rgba(0,0,0,0); -webkit-font-smoothing: antialiased; -moz-user-select: none; }
8     ul, ol { list-style: none; margin: 0; padding: 0; }
9     .cui-tab-mod { display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; height: 43px; line-height: 43px; border-bottom: #bcbcbc 1px solid; background-color: #fafafa; color: #666; font-size: 15px; position: relative; display: table\9; width: 100%; }
10     .cui-tab-mod li { text-align: center; -webkit-box-flex: 1; -moz-box-flex: 1; -webkit-flex: 1; -ms-flex: 1; flex: 1; display: table-cell\9; }
11     .cui-tab-mod li.cui-tab-current { color: #099fde; }
12     .cui-tab-mod .cui-tab-scrollbar { position: absolute; left: 0; height: 4px; bottom: -1px; background-color: #099fde; -webkit-transition: -webkit-transform 300ms ease ; transition: transform 300ms ease ; z-index: -1; -webkit-backface-visibility:hidden; backface-visibility:hidden;     }
13     .cui-tabnum2 { width: 50%; }
14     .cui-tabnum3 { width: 33.33333%; }
15     .cui-tab-mod li.cui-tab-current ~ .cui-tab-scrollbar { z-index: 2; }
16   </style>
17   <script id="others_zepto_10rc1" type="text/javascript" class="library" src="http://sandbox.runjs.cn/js/sandbox/other/zepto.min.js"></script>
18 </head>
19 <body>
20   <ul class="cui-tab-mod">
21     <li data-key="0" data-index="0" class="cui-item cui-tab-current">中国</li>
22     <li data-key="1" data-index="1" class="cui-item">美国</li>
23     <li data-key="2" data-index="2" class="cui-item">日本</li>
24     <i class="cui-tab-scrollbar cui-tabnum3"><span>1</span></i>
25   </ul>
26   <script type="text/javascript">
27     var wrapper = $('.cui-tab-mod');
28     var toolBar = $('.cui-tab-scrollbar');
29     var els = wrapper.find('li');
30     wrapper.on('click', function (e) {
31       toolBar.css({
32         '-webkit-transform': 'translate3d(100%,0,0)',
33         'transform': 'translate3d(100%,0,0)'
34       })
35       var el = $(e.target);
36     });
37   </script>
38 </body>
39 </html>
如果这样,我直接操作toolBar便可以绕过该BUG,但是我还是不太确定,所以有了这段代码:
View Code
1 var wrapper = $('.cui-tab-mod');
2 var toolBar = $('.cui-tab-scrollbar');
3 var els = wrapper.find('li');
5 wrapper.on('click', function (e) {
6   var el = $(e.target);
7   els.removeClass('cui-tab-current');
8   el.addClass('cui-tab-current');
9   toolBar.css('left', '0');
10 
11   //      setTimeout(function () {
12   //        toolBar.width('width', toolBar.width());
13   //      }, 0)
14 });
这一段代码也可以解决BUG,似乎解决bug的元素是导致了absolute元素的回流而与其它无关,这里再次对比Timeline:
两次对比没有什么差距,这个BUG导致的原因还需要深究,待我哪天去真机测试...... 
setTimeout动画
另外,最好也不要使用setTimeout之类的定时器来实现动画,下面这个动画操作也是非常消耗的:
这里如果使用requestAnimationFrame便可以避免Timer的影响。
fixed元素
一般来说,fixed元素在手机上尤其卡,我们这里也来追踪一下原因:
可以看到,fixed元素与left一样的都会不断的Paint,所以他卡呢,至于如何避免不同的团队有不同的方案。
结语
今天回顾了与宇果的交流过程中的渲染优化相关,这里还停留在比较浅的层次,需要继续学习,希望对各位有用,文中有误请您提出,最后微博求粉:
本文转自叶小钗博客园博客,原文链接:http://www.cnblogs.com/yexiaochai/p/4314260.html,如需转载请自行联系原作者

【前端优化之渲染优化】大屏android手机动画丢帧的背后相关推荐

  1. 买android手机,买大屏 Android 手机真的有必要吗?

    前言(废话) 我还记得第一次在店里见到初代三星 Galaxy Note 的时候,被它那块"巨型的" 5.3 英寸屏幕吓得愣了半天.我首先觉得它"大得不可思议", ...

  2. 【Android 性能优化】布局渲染优化 ( CPU 渲染优化 | 减少布局的嵌套 | 测量布局绘制时间 | OnFrameMetricsAvailableListener | 布局渲染优化总结 )

    文章目录 一. 减少布局嵌套 二. 布局渲染时间测量 1. FrameMetrics 使用流程 2. FrameMetrics 参数解析 3. FrameMetrics 代码示例 三. 布局渲染优化总 ...

  3. 安卓期末大作业——Android手机购物商城(含服务端)

    功能描述: 本系统带服务器端.服务器端代码是javaweb.服务器端主要实现了商品管理,用户管理,订单管理,留言管理等.手机端主要实现了用户注册,登录,商品查询,购物车,订单,留言等功能.适合新手学习 ...

  4. 前端性能优化之渲染优化

    1.知识体系 1.1从URL输入到页面加载 打开浏览器从输入网址到网页呈现在大家面前,背后到底发生了什么?经历怎么样的一个过程?现在带 大家来看看流程. 首先我们需要通过 DNS(域名解析系统)将 U ...

  5. 前端性能优化之资源传输优化、渲染优化、Web 加载和渲染原理

    一.资源传输优化 使用压缩 Gzip,如下所示: 对传输资源进行体积压缩,可高达 90% 配置 Nginx 启用 Gzip 启用 Keep Alive,如下所示: 一个持久的 TCP 连接,节省了连接 ...

  6. div自动滚动_从手机滚动丢帧问题,学习浏览器合成与渲染层优化

    一个 CSS 属性引发的血案 Web 页面性能是前端开发特别需要关注的重点,评判前端 Web 页面性能的指标有很多,页面的流畅度是其中的一种,如何让页面变得 "柔顺丝滑",要讨论起 ...

  7. android 省电优化,如何延长续航时间 浅谈Android手机的省电机制

    Android手机的续航能力为何普遍偏低,如今各大手机品牌又有哪些"补救手段"?为了很多Android手机在开启节能模式后却收不到微信消息?这一切,还得从手机的省电机制谈起. 为何 ...

  8. Vue常用的组件库大全【前端工程师必备】【实时更新】【移动端、PC端(web端)、数据可视化组件库(数据大屏) 、动画组件库、3D组件库】

    Vue常用的组件库大全[前端工程师必备] (一)移动端 常用组件库 1)Vant ui 2)Cube UI 3)VUX 4) NuTUI 5)Mint ui 6)Varlet UI 7)OnsenUI ...

  9. android 4.5屏幕,屏幕大也不怕 4.5吋起大屏续航手机推荐

    不知道大家有没有发现,随着智能手机硬件的快速发展,CPU从单核1GHZ跳跃到双核再到四核,手机屏幕也是不断从3.5英寸升级到了5.5英寸,手机的待机问题成为了一个新的难题,同时也制约了手机的发展.超大 ...

最新文章

  1. axios的简单使用
  2. 安装了email模块还是报错_科普:利用Python smtplib和email模块实现自动发送邮件功能...
  3. Javascript--File对象
  4. TextView中实现部分文字点击
  5. Andorid APK反逆向
  6. python-九九乘法打印
  7. matlab2c使用c++实现matlab函数系列教程-normpdf函数
  8. maven scala plugin 使用教程笔记之jvmArgs配置-D参数中使用.md
  9. W,b的初始化和几种激活函数
  10. linux安装gcc详细过程,linux下安装GCC
  11. 找特征点的算法 SIFT和SURF算法
  12. 核方法(kernel Method)
  13. Hibernate事务与并发问题处理(乐观锁与悲观锁)【转】
  14. 算法复杂度的计算方法
  15. jquery stop()方法
  16. 艺术照片特效软件JixiPix Premium Pack Mac2020-10-15
  17. 一个计算机爱好者的不完整回忆(十)插播游戏
  18. html中快速返回上一个页面,后退一步!
  19. lesson5画表情包
  20. 关于克苏鲁神话的细节

热门文章

  1. openssh arm linux 编译,交叉编译openssh
  2. 关于 CSS 的英文单词换行 (word-break 和 word-wrap 的区别)
  3. 阿里巴巴集团《首届中国农民丰收节电商数据报告》发布
  4. JavaScript深拷贝函数
  5. StarUML破解方式
  6. 这样学还不会?小白入门编程第1讲 计算机基础知识 JAVA基础知识 必看
  7. Yarn三种调度策略对比
  8. java计算机毕业设计基于安卓Android的天文观星系统app uniapp 小程序
  9. 《侍道外传:刀神》:一款优秀的ARPG,如何让武士沦为“还债的神”?
  10. 基于Vue3,Element Plus的一款表单设计器,支持生成Element和Antd样式的表单