在DevTools中开始渲染,向下滑动一点点滚动条,然后停止滚动。
在结果中,注意frames总是在30ftps线上面,甚至都木有很接近69ftps线的(事实上帧执行的太缓慢以致于60ftps线在图上都不显示)
打开一个帧可以看见在scrol事件之后是一个函数回调,然很多分离的layout事件,每个分离的layout事件都有一个红色的小三角形,红色小三角形是强制同步layout( a forced synchronous layout )执行的标记。
Note
- 被强制的synchronous layout发生在当浏览器在一个脚本中运行layout的时候,强制重新计算样式,所以需要再次运行layout。这个典型的发生在循环内部,就像下面的代码展示的一样,在一系列div中循环且重新设置它们的宽度属性,强制synchronous layout。
var newWidth = container.offsetWidth; divs.forEach(function(elem, index, arr) { elem.style.width = newWidth; })
这里有很多css属性可以导致layout被触发;查看会触发layout的属性:CSS Triggers.
查看layout事件的详细信息,你可以看见被强制的同步layout提醒是被app.js中的colorizeAndScaleStories函数触发。
让我们检查函数
function colorizeAndScaleStories() { var storyElements = document.querySelectorAll('.story'); // It does seem awfully broad to change all the // colors every time! for (var s = 0; s < storyElements.length; s++) { var story = storyElements[s]; var score = story.querySelector('.story__score'); var title = story.querySelector('.story__title'); // Base the scale on the y position of the score. var height = main.offsetHeight; var mainPosition = main.getBoundingClientRect(); var scoreLocation = score.getBoundingClientRect().top - document.body.getBoundingClientRect().top; var scale = Math.min(1, 1 - (0.05 * ((scoreLocation - 170) / height))); var opacity = Math.min(1, 1 - (0.5 * ((scoreLocation - 170) / height))); score.style.width = (scale * 40) + 'px'; score.style.height = (scale * 40) + 'px'; score.style.lineHeight = (scale * 40) + 'px'; // Now figure out how wide it is and use that to saturate it. scoreLocation = score.getBoundingClientRect(); var saturation = (100 * ((scoreLocation.width - 38) / 2)); score.style.backgroundColor = 'hsl(42, ' + saturation + '%, 50%)'; title.style.opacity = opacity; } }
注意height,width和line-height被计算,所有的这些属性将会导致layout被触发。
透明度属性也被设置了——但是透明度属性不会触发layout——这一行代码应用了新的样式,所以会触发样式的再次计算以及layout。
上面两个技术用于函数的主循环中将会导致强迫的同步layout问题。
虽然我们可以通过css样式修改来替代js,但是我们最好还是完全去除这些效果:最简单的就是——有时候最好的代码设置就是删除代码。
让我们去除上面的所有代码再次查看,你可以看见60ftps,性能好多了。
额外的layout和强制的同步layout提醒不存在了,帧执行的速度也很棒!