【翻译】MobileWeb开发的性能调优技巧

iScoll这个开源组件的作者写了一篇心得 ,分享了他对前端性能优化的经验,这里翻译一下,有不错的参考作用。


首先,不要迷信“性能优化技巧(performance tricks)”之类的文章,包括你在看的这篇。每个场景都是不同的,而且在一处有效并不代表在另一处也有效。关于性能优化的唯一真理是:测试。很多测试,经常测试。尝试不同的技术,哪怕是理论上不会有更好性能的,哪怕是JS官方指引不推荐使用的。

所以,第一条技巧就是:通过各种方法测试你的代码,还要经常测试,因为当今世界实在变化太快了(iOS都到8了)。

我准备了一个非常简单而又非常错误的 demo 。这个demo演示了当你关注性能的时候,你所不应该做的事情。我们将要向这个demo添加一系列的优化技术,以达到我们期望的最佳的FPS指标(FPS,Frame Per Second,每秒帧数)。

我在使用一个ipad作为测试机,所以多次的测试结果是连续的,并且是和设备相关的。我一直在强调移动开发,但是我所说的所有内容对于桌面的web开发也是适用的。移动开发也能让我们变成一个更好的桌面Web开发者。

刚刚提到的 反面例子demo,在retina-ipad上他的FPS大概在8左右(@#¥%……&#@¥@)


KEEP IT SIMPLE, KEEP IT FLAT

尽量保持简单,保持扁平

让一切总是从坚实的基础上开始。让你的DOM结构保持简单,整体上减少你的DOM元素数量,如果能用一个绝对不用两个。

I know that your inner designer will shiver(求翻译),但有时候一张图片会比一系列复杂的CSS属性更好更高效。请记住:阴影和透明效果都会极大影响性能。在demo中,我们只是简单地移除了透明效果,fps就从8飙升到13了,如果我们把box-shadow也移除,将能达到更高的FPS。

但我们要怎么才能保留阴影和透明效果,而不影响渲染呢?就像我刚刚说的:用一张图片。在其他场景也同样适用,我知道你不想只为了一个阴影做画一张图片,但要做到性能最大化,工作就必然是费劲的,而且也未必会是你喜欢的方式。

更新过的demo 中,我将三个拥有很多样式的元素替换成一张图片。如果你使用chrome来看demo,会发现FPS已经从40升到60了,说明方向是正确的,但是在ipad上还是显得性能比较低下。

这将引导我们走向下一步。


DON’T TOUCH THE DOM!

不要触碰DOM树

当一个DOM元素离开可视范围后被销毁,然后一个新的被创建,这对浏览器而言是一个明显的压力。

提前创建你需要的元素,并按需重用这些元素。尤其在一次大幅度的渲染过程中,不要改变DOM结构。在最近的一次实验中,我发现了点击事件并不喜欢 createElement语句。当你开始玩弄HTML的结构之时,JS内部的事件发射器会被你激怒的。

所以我们的 demo 调整成一开始创建50个元素并且一直在重用。FPS从8上升到14。表现不错,但还是不够快。


CACHE EVERYTHING

JS真的很擅长于缓存。所有可以缓存的东西,你都应该缓存起来……实际上JS能缓存所有东西。(译者:其实不必要担心缓存会占用太多内存,这里空间消耗其实不大,换来时间的提升却很明显)。

我正在做一件很脏的事情:在一个循环里执行querySelectorAll。对DOM的查询结果应该被缓存起来,所以 下一步 ,我打算把查询结果的元素应用保存到一个数组里面。但不止这样,若我们需要大量地高频地使用style属性,我也可以将它缓存起来。我添加了一个styles数组来保存起所有目标元素的style属性。

对于缓存你还可以做得更进一步。你可能看过这样的代码:

for ( var i=0, l=els.length; i……
}

这里我们将数组的长度缓存到一个变量中。这里还可以做得更有创造性一点,譬如如果你需要遍历一个对象,你可以将hasOwnProperty缓存起来;如果你需要使用Math里面的方法,你可以将你需要的方法缓存起来(sqrt/atan/random……)(译者:这有点过了吧……)。

底线是:将所有可以的需要的东西缓存起来,尤其在一个循环里,尤其那些和DOM有关的。

修改后的 新demo,在桌面浏览器上能达到180FPS,但在我们喜欢的iPad上仍然显得慢。


USE HARDWARE ACCELERATED CSS PROPERTIES (WITH A CATCH)

使用硬件加速的CSS属性(需要在catch里面)

在这点上我们要尽量利用GPU(图像加速处理单元)的力量,和使用硬件加速的CSS属性。在这个demo中,就是要使用 transform:translateX() (我们还加上translateZ(0) 来保证元素真正被硬件加速了)。

最后一次修改后的demo,在桌面浏览器上达到了210FPS,在iPad上也有可接受的30FPS(记住,这个iPad是上几代产品了)。这真的让人印象深刻,但却是有代价的。硬件加速是一件非常费力的事情,而且需要小心处理。

你不能把赌注都压到硬件层,因为你没有足够的资源;如果你这样做,浏览器最终会不幸崩溃。所以,要让浏览器保持轻型。不要使用硬件层除非你真的需要,除非你需要真的的圆滑的过渡动画。不要马上就把top/left抛弃掉,尽量去使用它们,而当你真正需要的时候,要记得有 transform


REQUEST ANIMATION FRAME IS(NOT) YOUR MASTER

目前我们都在使用setTimeout,但你可能听说过requestAnimationFrame这剂神药。应该这样说吧,requestAnimationFrame提供了一个比setTimeout/Inteval 更可靠的循环控制。这个循环是和屏幕刷新频率一致的,给我们带来无痛的顺滑的动画。但你在这里要听到一个“但”字。

setTimeout 很丑陋但是它最有效, requestAnimationFrame则按固定步长前进。后者的好处是使用浏览器更轻松,前者的好处则是他会在CPU空闲的第一时间执行处理。

我不是在建议你放弃requestAnimationFrame,只是再次提醒你需要做测试。就像在我们的demo中,FPS从30变成25了。5个FPS对于渲染器而言很轻松,但还是会有一些场景你会需要使用原始的timeout来完成。


USE TRANSITIONS

CSS属性中的补间动画(Tweening)通常不是一个好主意。目前我们会每个周期小量地改变元素的位置。我们让浏览器每16微秒重画屏幕50次。

通过transitions,每个元素只需修改一次样式属性,但若不使用transtions则每秒要做3000次。这区别真的很大。

这个transition的demo可能是我们当前的设置能达到的最快速度。我把FPS计算器去掉了因为我们不再在一个循环里了。

所以基本上要避免使用补间动画


GOING FURTHER

我们通过6步的优化让demo在iPad上从8FPS提升到60,这成绩已经不错了但是我们仍然可以做一些事情去优化我们的代码。这些也许跟我们的demo没有直接相关,但是很值得说一下。


GIVE IT A REST

关于移动浏览器,有一件最终的事情你必须知道的,就是它的懒。对元素样式的修改是永久生效的,你应该给在执行DOM的大量修改前,给渲染器一些休息时间。

一个很常见的场景:

var x = 100;

setTimeout(function () {
    element.style.left = x + ‘px‘;
}, 100);

如果你的代码不知道为什么就是不生效,试试给它一些休息时间。你会惊喜的。

我还发现一些复杂的代码执行,最好要从dom加载好后等个300ms才触发。


KEEP EVENT LISTENER CALLBACKS LIGHT

事件是很让人讨厌的。尤其是 touchmove 和 devicemotion。他们就像疯了一样被触发,而且他们对DOM的变更很敏感。基本上在事件回调函数上不要做任何事情,只保存一个变量并且在稍后用到它,也许是在一个requestAnimationFrame的循环里。

看看这些伪代码

var x;

element.addEventListener(‘touchmove‘, function (e) {
    x = e.changedTouches[0].pageX;
}

function loop () {
    requestAnimationFrame(loop);
	
    element.style.left = x + ‘px‘;
}

还有记住,不要使用 createElement 如果你在侦听touch事件。从来不要。


EVENT LOADING/UNLOADING

我经常会看到,基于用户行为而对事件进行加载/卸载。很常见的就是在touchStart的时候加载touchMove,在touchEnd的时候卸载touchMove。

这样做有意义,但是如果你遵循上述的规则(让事件回调函数保持轻型),我感觉最好还是让事件一直注册不卸载。这在旧的安卓设备上,尤其明显。


DON’T USE UNNECESSARY ADD-ONS

不久前我看到一个网站的代码这样写:

width = $(window).get(0).innerWidth;
这样等JQuery全部加载完才能用,真的吗?!

我们都很习惯于使用JQuery而甚至都不停下来思考一秒。所有人都喜欢钱,但你随意地使用钱并不一定让你变得更健康。

我很欣赏那些很牛很厉害的第三方库,但是真的只选择你需要的那些就行了。

我只是起个头,如果觉得有趣我可以写得更深入,你们的优化技巧呢?

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。