Web性能优化系列:预防布局抖动
Posted 前端大全
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Web性能优化系列:预防布局抖动相关的知识,希望对你有一定的参考价值。
预防“布局抖动”
布局抖动是因 javascript 的 DOM 元素被多被次暴力写,然后读,导致文档重排而出现的。
// 读
var h1 = element1.clientHeight;
// 写(无效布局)
element1.style.height = (h1 * 2) + 'px';
// 读(触发布局)
var h2 = element2.clientHeight;
// 写(无效布局)
element2.style.height = (h2 * 2) + 'px';
// 读(触发布局)
var h3 = element3.clientHeight;
// 写(无效布局)
element3.style.height = (h3 * 2) + 'px';
当DOM元素被写入值,布局就“无效”,而多次这样就会导致文档重排。浏览器很懒,它总想等到当前操作(或帧)的最后一步才重排。
然而,如果在当前操作(帧)完成前,从DOM元素中获取值,这会迫使浏览器提早执行布局操作,这称为“强制同步布局”,这可是性能杀手!
布局抖动的副作用在现代桌面浏览器上并不明显;但对于低配置的移动设备来说,其后果就不堪设想了。
能快速修复?
在理想情况下,我们可能通过简单地重复执行,以至于将DOM元素的读写操作放在一起执行。这意味着文档只需重排一次即可。
// 读
var h1 = element1.clientHeight;
var h2 = element2.clientHeight;
var h3 = element3.clientHeight;
// 写(无效布局)
element1.style.height = (h1 * 2) + 'px';
element2.style.height = (h2 * 2) + 'px';
element3.style.height = (h3 * 2) + 'px';
// 文档在最后一帧将进行重排
现实情况会怎么样?
现实情况并非如此简单。大型应用程序的代码会分散到各个地方,因此这些地方都有危险的DOM操作。所以不能简单地(绝对不应该)聚集它们,而需要解耦代码,只是需要控制好执行顺序。那如何让读写操作捆绑在一起,从而获得最佳性能呢?
进入requestAnimationFrame
window.requestAnimationFrame是一个将操作安排在下一帧一起执行的函数,类似于setTimeout(fn, 0)。这是非常有用的,因为能使用它来安排所有DOM的写操作在下一帧一起执行,保留所有DOM的读操作在当前同步状态。
// 读
var h1 = element1.clientHeight;
// 写
requestAnimationFrame(function() {
element1.style.height = (h1 * 2) + 'px';
});
// 读
var h2 = element2.clientHeight;
// 写
requestAnimationFrame(function() {
element2.style.height = (h2 * 2) + 'px';
});
这意味着我们能很好地封装代码了。经过小小调整后的代码,就将高耗能的DOM操作捆绑在一起!实在太棒了!
工作实例
我创建了一个工作案例来证明这个观点。从第一个截屏的chrome时间轴可看出,有多个布局抖动穿插其中。
在改用requestAnimationFrame 后,仅仅只触发一次布局事件,其结果是操作快了约96%。
它具有伸缩性吗?
在一个简单案例里,使用requestAnimationFrame来延迟DOM写操作,从而大大提高性能,但这项技术没有伸缩性可言。
在我们的应用中,可能需要在DOM元素上执行先写后读操作,然后再次掉入布局抖动的坑,只是在不同帧。
// 读
var h1 = element1.clientHeight;
// 写
requestAnimationFrame(function() {
element1.style.height = (h1 * 2) + 'px';
// 我们可能想在设置高度后再读取新高度值。
var height = element1.clientHeight;
});
我们可以将读操作放到另外一个requestAnimationFrame ,但我们不能保证应用程序的另一部分,没有把写操作放在同一帧上。
介绍 ‘FastDom’
FastDom是一个轻量的库,它提供一个公共接口,能让DOM的读/写操作捆绑在一起。其实,它就是利用上述同样的 requestAnimationFrame 技术来大大提高DOM操作速度。
fastdom.read(function() {
var h1 = element1.clientHeight;
fastdom.write(function() {
element1.style.height = (h1 * 2) + 'px';
});
});
fastdom.read(function() {
var h2 = element2.clientHeight;
fastdom.write(function() {
element2.style.height = (h1 * 2) + 'px';
});
});
FastDom通过接收读写操作,并在下一帧捆绑它们(先读后写),从而消除DOM的相互影响。这意味着我们能独立编写应用程序组件,而不用担心它们在应用程序中互相影响。
使用FastDom的启示
通过使用FastDom,会让所有DOM任务变成异步,这意味着你不能总是假设DOM将会以什么状态进行操作。操作从之前的同步,变成现在的异步方式。因此,可能没执行完异步处理函数就会执行下一步操作了。
要解决这一点,我打算用事件系统来明确操作何时完成,和明确依赖于完成后所做出的响应操作。
虽然所做工作是一样的,但能通过增加代码量来显著提高性能。我个人认为这个代价小。
FastDom案例
Animation example:http://wilsonpage.github.io/fastdom/examples/animation.html
Aspect ratio example:http://wilsonpage.github.io/fastdom/examples/aspect-ratio.html
完善FastDom
web应用缺少一个明确的方式,来解决布局抖动问题。正如一个应用程序很难协调所有不同的部分,来确保产品最终是高效的。如果FastDom能为开发者们提供一个简单接口来解决这个问题,那只能意味着它是个好东西。
瞧一瞧 FastDom 项目,欢迎随时通过 pull requests 或 filing issues 来完善它。
原文出处:wilsonpage.co.uk
译文出处:伯乐在线 - 刘健超-J.c
链接:http://web.jobbole.com/82546/
1.『前端大全』分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯,相关职位。欢迎关注。
http://web.jobbole.com/all-posts/
2. 点击“阅读原文”,查看更多前端文章。
以上是关于Web性能优化系列:预防布局抖动的主要内容,如果未能解决你的问题,请参考以下文章
Web性能优化系列:10个JavaScript性能提升的技巧