文档结构  
翻译进度:54%     翻译赏金:0 元 (?)    ¥ 我要打赏

首先。不要相信“性能技巧”的帖子。没错,包括这一条。每个脚本都是互不相同的,适应于一种安装程序的脚本不一定适用于另一个。有关优化的唯一普遍真理是:测试。大量测试,频繁测试。尝试不同的技术,包括那些理论上性能不大好的测试技术,甚至是那些JS大会中不被看好的测试技术。

所以第一个技巧是: 用各种不同的方式测试你的代码,而且要频繁测试 ,因为在这个错综复杂的世界里一切都变化得非常迅速。

出于写这篇文章的需要,我使用了一个非常简单但错严重误的演示。该演示几乎囊括了在测试性能时不应该做出的所有行为。我们将采用一系列优化技术来使这个演示获得最好的帧速率。我并不打算逐行地审查代码,看看源代码,也就几行。

第 1 段(可获 2.03 积分)

我以iPad作为测试装置,这样测试基准就是一致的,不会随装置而变化。虽然我这里重点强调移动开发测试,但对于桌面浏览器也一样有效。移动开发测试对我们成为更好的桌面开发者同样有所助益。

这是一个错误严重的演示。目前iPad的视网膜显示屏帧速率大约为8fps (天哪!)。

保持简化,保持扁平化。

要有坚实的七点。将DOM保持在简单状态,把元素树状图删减到只有枝干,能用一个元素的地方不用两个元素。

我知道作为设计者你的内心在颤抖,但有时使用单张图像代替一系列复杂的CSS属性会更好。记住,阴影和透明度对性能影响很大 。只要简单地去除透明度属性,我们的帧速率就从8fps提升到了13fps。如果我们再去除盒装阴影属性,就又能释放出一些FPS。

第 2 段(可获 1.94 积分)

但是如何在不使用渲染器的情况下得到透明度和阴影效果呢?正如我所说:使用图像。对于许多其他场景也是如此。我知道你不喜欢为了一个 阴影去作图(我也没说过你会喜欢),但是通往高性能的路是条上坡路。

在这个最新的示例中 我仅用一张背景图来代替三个样式元素来取得丰富的样式效果。如果你在Chrome上查看我们的示例,我们已经将帧率从40提到60. 所以这个大方向是正确的。不过,iPad上的性能表现仍然很低。

由此我们进入下一步。

第 3 段(可获 1.45 积分)

不要碰 DOM!

如果在行结束的时候销毁某个元素并创建新的,这对浏览器来说压力实在太大了。

提前创建所有需要的元素,并在需要的时候重复使用它们。不要改变 DOM,尤其是在密集渲染的情况下。在最近一次实验中,我们进行了尝试,发现触摸事件并不喜欢 createElement。如果你开始操作 HTML 结构,内部事件会变得很糟糕。

我们的演示创建了 50 个元素并一直重复使用它们,FPS 从 8 变为 14。非常不错的进展,不过仍然很慢。

第 4 段(可获 1.3 积分)

缓存一切

JavaScript 真正利用了缓存的优势。你应该缓存任何可以缓存的东西……实际上在 JS 中什么东西都可以缓存。

我在演示中干了件非常可恶的事情,在循环中使用 querySelectorAll。查询到的 DOM 应该总是被缓存,这样下一次迭代我会保存一个元素数组的引用,但不会仅限于此。既然我们会集中使用 style 属性,我们应该也把它缓存起来。我添加了一个 styles 数组用来保存所有目标元素的 element.style。

你可以非常积极的使用缓存。也许你见过这个:

第 5 段(可获 1.34 积分)
for ( var i = 0, l = els.length; i < l; i++ ) {
...
}

这里我们把数组长度缓存到一个变量中。这带来了启发,如果你需要遍历对象,你可以缓存 hasOwnProperty,使用 Math 的时候也可以缓存需要的函数(sqrt/atan/random/...),诸如此类。

最后一条是:缓存可以缓存的一切,尤其是在循环内部,与 DOM 相关的东西更应该如此。

新的演示结果在PC上非常惊人地达到了 180FPS(如果去掉 60FPS 的约束),但它在我们心爱的 iPad 上仍然很慢。

第 6 段(可获 1.18 积分)

Use Hardware Accelerated CSS properties (with a catch)

At this point we have to unleash the power of the GPU and use hardware accelerated CSS properties. In this case transform:translateX() (and we add translateZ(0) to ensure that the element is really hardware accelerated).

This last change skyrocketed the demo to 210 FPS on desktop and an acceptable 30 FPS on iPad (remember it's an old gen device). This is pretty impressive but it comes to a price. Hardware acceleration is very demanding and should be handled with care.

You can't put the world on the hardware layer, you don't simply have enough resource for that; the browser will eventually and unexpectedly crash! Keep the browser light. Don't use the hardware layer unless you really need it and only if you need really smooth transitions. Do not throw top/left away just now, actually keep using them as much as you can knowing that when you really need it, there's transform.

第 7 段(可获 2.03 积分)

requestAnimationFrame is (not) your master

So far we used setTimeout, you may have heard of the magic pill requestAnimationFrame. An article of its own should be dedicated to it, just let's say that requestAnimationFrame offers a more reliable loop than setTimeout/Interval does. The loop is also aligned with the screen refresh rate, resulting in tearing-free, smooth animations. And here you can hear the sound of a "but".

setTimeout is awful but it's best effort, requestAnimationFrame goes at a constant pace. The good of the latter is that the browser is more relaxed, the benefit of the former is that it is executed as fast as the CPU can do.

第 8 段(可获 1.38 积分)

I'm not suggesting to drop requestAnimationFrame, just (again) make your own tests. With the polite rAF our demo goes at 25FPS instead of 30 for example. 5 FPS are well worth a relaxed renderer, but still there are situations where you might want to use the plain old timeout.

Use transitions

Tweening CSS properties is not usually a good idea. So far we are altering the position of the elements by a small amount at every cycle. We are asking the browser to repaint the screen 50 times every 16ms.

With transitions we change the style property just once per element instead of 3000 times per seconds. That's a huge difference.

第 9 段(可获 1.41 积分)

The transition demo is probably the fastest we can go with the current installation. I removed the FPS count because we are not in a cycle anymore.

So basically avoid using tween libraries.

Going further

We've gove from 8FPS to 60 in just 6 steps, not bad but there are other things we could do to uber optimize our code; they are not directly applicable to this demo but are worth mentioning nonetheless.

Give it a rest

The most important thing you will learn about the mobile browser is that it is lazy. Changes to element styles take forever (in machine time) and you should always give the renderer a rest before executing multiple variations to the DOM.

第 10 段(可获 1.49 积分)

一个常见场景是:

var x = 100;

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

如果你的代码无故罢工,就试着让浏览器休息一下。你会吃惊的。

我还发现,有时候最好在文档加载之后至少等300ms,再运行复杂的代码。

然事件监听器的回调函数保持轻便

各种事件就是杂种,touchmovedevicemotion事件尤其如此。他们疯狂地触发,对DOM的改变又十分敏感。基本上来说,除了保存变量,就不要在事件回调中做任何事情,以后再使用这个变量的值,比如在requestAnimationFrame的循环中。

第 11 段(可获 1.18 积分)

Really fake pseudo code:

var x;

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

function loop () {
    requestAnimationFrame(loop);

    element.style.left = x + 'px';
}

Also remember: don't use createElement if you are listening to touch events. Ever.

Event loading/unloading

A thing that I often see doing is event loading/unloading based on user action. Very common is loading the touchMove event on touchStart and unloading it on touchEnd.

It makes sense, but if you follow the above rule (Keep event listener callbacks light), I feel it is better to just leave the events registered. This is especially true on older Android.

第 12 段(可获 1.03 积分)

Don't use unnecessary add-ons

Lurking a website code I found this not long ago:

width = $(window).get(0).innerWidth;

That's all jQuery was loaded for. Really?!

We are so accustomed to jQuery (and many other tools) that we do not even stop thinking for a second. It must be something related to the $, everybody loves money, but you don't get wealthier by using $ indiscriminately.

I'm all for using super sassy libraries, just pick the ones you really need, FGS!

I just scratched the surface, if there's interest I could go even deeper, what are your optimization tricks?

第 13 段(可获 1.21 积分)

文章评论