欢迎关注个人我的公众号,不按期更新本身的工做心得。css
题记:能够把这篇文章当作一次事故记录,一次线上事故。此刻我能坐在这里写下这篇文章,要感谢我司没有开除我,或者说我解决了此次性能问题,因此我有机会写下这篇文章。html
我司全球购业务改版,将原生的全球购页面采用Hybird模式开发,这是我司H5页面第一次出如今以及入口位置,做为开发者显得非常兴奋。html5
根据设计稿,如期完成工做,测试也已准备好,部分Android低端机中显示出卡顿现象,出于对自身技能的确定,误觉得是硬件性能问题,并未引发过多关注。ios
未引发重视,并无作出相应处理。web
上到pre环境后,发现部分iPhone机型出现崩溃,初步排查在iOS 8.4系统上DOM节点高度达到10000px以上奔溃率100%,其余8-9之间的系统版本当分页数据使得DOM达到10000px以上快速滚动会出现卡顿,9以上iOS系统与Android4.4以上系统均表现良好。定位问题后,迅速进行更多特殊机型与相应系统的测试,期间借遍了公司几百位小伙伴的手机,在此向他们表示感谢。chrome
你问我为何,我只能说测试环境没有这么多数据,至于为何没有?呵呵,你懂得!浏览器
iOS将UIWebview切换为WKWebview,WKWebview为iOS在8以上系统中新加入的一个高性能Webview,具体哪里好,能够点这里 这里 还有这里 ,因为iOS已经发版(对的,在H5页面还在pre环境测试的时候,iOS已经发版了)这个方案只能做为下一个版本了性能优化
临时下降数据显示总量,同时寻找性能瓶颈(这里要和运营的同窗说声sorry)微信
针对iOS 8如下以及8.4系统 Android 4.4如下系统作自动降级处理。框架
测试不能仅仅作功能测试,还要作性能测试
开发人员常使用chrome、Firefox等浏览器的开发工具进行开发,这里要注意的是:浏览器开发工具只能模拟手机UI与交互,其性能要远远强于手机环境。
开发过程当中经常使用微信、QQ、手机浏览器进行测试,这是不可取的,由于最终环境不一,容易给本身形成错觉,以为在微信 QQ的Webview中没问题,就OK了
deadline 真的要好好定。不少人认为H5很灵活,并不依赖于iOS发版,能够将测试定在iOS发版的那几天进行。其实这真的是巨大的错误,后面我会说明。
通过降级后,仍然存在不少问题,同时咱们也在抓紧研究后续升级方案。通过协调,将系统切回了老版本暂时使用原生替代H5(感受很受打击~)。
数据量不足,对运营影响很大
对降级机型的体验不好,数据统计显示,受影响的机型仍是有必定份额
因为这次H5是存在于以及目录,因此设计上是常驻内存以保证体验,UIWebview的内存一直得不到释放,同事系统中其余页面也使用到了UIWebview,长时间使用仍然会出现崩溃。
距离iOS和Android下一次发版还有一段时间,不可能一直降级
不管如何解决性能问题,这是惟一目标!
通过一个星期枯燥而漫长的努力与实验,从如下几个方面完全解决了性能瓶颈;
iScroll是咱们内部框架中引入的一个第三方组件,官方的介绍是「Smooth scrolling for the web」,奈何在大数据量面前性能确实使人堪忧。若是数据量少,仍是推荐使用的。
在对比了竞品,和天猫、JD等知名厂商H5实现后,确实郁闷了一段时间,一样的数据量为何别人能够作到?最后获得两个结论:
别人DOM节点没咱们复杂,因为咱们采用了内部研发的m-fast框架,框架顶层就肯定了DOM结构的复杂性,例如:框架预设了整个布局为上、下、左、右、中的布局,即便个人页面只是一个流式布局。
别人H5页面没有图片轮播!对,你没看错,一个图片轮播竟然对性能影响如此之大
居于上面这两点,我开始了DOM性能研究
m-fast框架采用模板预编译技术,将页面模板编译为js文件,在经过异步加载的方式载入,因此,网页的渲染大体符合上面这五个步骤
JavaScript。通常来讲,咱们会使用JavaScript来实现一些视觉变化的效果。好比用jQuery的animate
函数作一个动画、对一个数据集进行排序、或者往页面里添加一些DOM元素等。固然,除了JavaScript,还有其余一些经常使用方法也能够实现视觉变化效果,好比:CSS Animations, Transitions和Web Animation API。
计算样式。这个过程是根据CSS选择器,好比.headline
或.nav > .nav_item
,对每一个DOM元素匹配对应的CSS样式。这一步结束以后,就肯定了每一个DOM元素上该应用什么CSS样式规则。
布局。上一步肯定了每一个DOM元素的样式规则,这一步就是具体计算每一个DOM元素最终在屏幕上显示的大小和位置。web页面中元素的布局是相对的,所以一个元素的布局发生变化,会联动地引起其余元素的布局发生变化。好比,``元素的宽度的变化会影响其子元素的宽度,其子元素宽度的变化也会继续对其孙子元素产生影响。所以对于浏览器来讲,布局过程是常常发生的。
绘制。绘制,本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等,也就是一个DOM元素全部的可视效果。通常来讲,这个绘制过程是在多个层上完成的。
渲染层合并。由上一步可知,对页面中DOM元素的绘制是在多个层上进行的。在每一个层上完成绘制过程以后,浏览器会将全部层按照合理的顺序合并成一个图层,而后显示在屏幕上。对于有位置重叠的元素的页面,这个过程尤为重要,由于一旦图层的合并顺序出错,将会致使元素显示异常。
若是你修改一个DOM元素的“paint only”属性,好比背景图片、文字颜色或阴影等,这些属性不会影响页面的布局,所以浏览器会在完成样式计算以后,跳过布局过程,只作绘制和渲染层合并过程。
若是你修改一个非样式且非绘制的CSS属性,那么浏览器会在完成样式计算以后,跳过布局和绘制的过程,直接作渲染层合并。这种方式在性能上是最理想的,对于动画和滚动这种负荷很重的渲染,咱们要争取使用这种渲染流程。
绘制,是填充像素的过程,这些像素将最终显示在用户的屏幕上。一般,这个过程是整个渲染流水线中耗时最长的一环,所以也是最须要避免发生的一环。
CSS属性中,除了transform和opacity以外,修改任何属性都会触发绘制
若是布局被触发,那么接下来绘制必定会被触发。由于改变一个元素的几何属性就意味着该元素的全部像素都须要从新渲染!
若是改变元素的非几何属性,也可能触发绘制,好比背景、文字颜色或者阴影效果,尽管这些属性的改变不会触发布局。
绘制并不是老是在内存中的单层画面里完成的。实际上,浏览器在必要时将会把一帧画面绘制成多层画面,而后将这若干层画面合并成一张图片显示到屏幕上。
这种绘制方式的好处是,使用tranforms来实现移动效果的元素将会被正常绘制,同时不会触发对其余元素的绘制。
在页面中建立一个新的渲染层的最好方式就是使用CSS属性will-change
,Chrome/Opera/Firefox都支持该属性。同时再与transform
属性一块儿使用,就会建立一个新的组合层:
.moving-element { will-change: transform; }
对于那些目前还不支持will-change
属性、但支持建立渲染层的浏览器,好比Safari和Mobile Safari,你可使用一个3D transform属性来强制浏览器建立一个新的渲染层:
.moving-element { transform: translateZ(0); }
但须要注意的是:不要建立太多的渲染层。由于每建立一个新的渲染层,就意味着新的内存分配和更复杂的层的管理。
上面提到的,因为框架的限制,页面布局相对需求来讲复杂不少。开发中应该尽可能避免复杂的DOM结构,复杂的DOM结构更容易引发大面积的重绘。
渲染层的合并,就是把页面中完成了绘制过程的部分合并成一层,而后显示在屏幕上。
使用transform/opacity来实现动画效果,目前只有transforms和opacity这两个属性不会触发浏览器的布局和绘制,对网页元素这两个属性的修改会直接触发渲染层合并。
对于动画效果的实现,避免使用setTimeout或setInterval,请使用requestAnimationFrame。
把耗时长的JavaScript代码放到Web Workers中去作。
这里可使用Chrome DevTools的Timeline和JavaScript Profiler来分析JavaScript的性能。
性能优化是一门作减法的艺术。咱们首要要尽力简化页面渲染过程,而后要使渲染过程的每一步都尽可能高效。