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性能提升的技巧

Web性能优化系列:剖析页面绘制时间

Web性能优化系列:把性能看作设计的一部分

分布式技术专题「系统服务优化系列」Web应用服务的性能指标优化开发指南(基础篇)

Web前端性能优化-重绘与回流

打个总结:Web性能优化