读写style对渲染过程的影响
一直都说操作DOM性能很差,那么为什么差呢?读写style会给性能造成什么影响?本文作为渲染性能的第一篇(从发布时间来看)其实不太合适,第一篇应该先和大家简单介绍浏览器对于页面的渲染过程,后面会补上,恰好今天写了一些读写style的demo,于是提前写了这篇博客。
参考:全部来源于Google最新web的开发者文档:
关于timeline的简单使用,可以参考之前的一篇文章:
实验过程
实验过程非常简单:操作元素的style,然后通过timeline观测渲染过程发生了什么变化。
<!DOCTYPE html>
<html>
<head>
<title>demo</title>
<style type="text/css">
.s-1 .read:nth-child(1) {
width: 500px;
background-color: green;
}
.s-1 .read:nth-child(2) {
width: 300px;
background-color: blue;
}
.s-1 .read:nth-child(3) {
width: 100px;
background-color: red;
}
</style>
</head>
<body>
<section class="s-1">
<div class="read">A: test to see what happen</div>
<div class="read">B: test to see what happen</div>
<div class="read">C: test to see what happen</div>
</section>
<script type="text/javascript">
// 这里style的改变特指几何属性(width,height),位置属性(margin...)的改变,不包括(opacity,color...)
window.onload = () => {
// 确保在合适的时候执行(关于requestAnimationFrame会在之后再写一篇文章来介绍)
requestAnimationFrame(()=>{
let divs = document.querySelectorAll('section .read');
let [a, b, c] = divs;
// 结论一:连续读取同一个元素的高宽(多次读取也一样)只会触发一次layout,底层应该调用了getBoundingClientRect()
// const width = a.offsetWidth;
// const height = a.offsetHeight;
// 等同于上面
// a.getBoundingClientRect();
//结论二:连续读取不同元素的高宽,只会触发一次layout,底层在layout的时候应该算出了所有元素的位置,下次再读取(没有写入的前提下)不会触发新的layout
// const sizeA = a.getBoundingClientRect();
// // console.log(sizeA)
// const sizeB = b.getBoundingClientRect();
// // console.log(sizeB)
// const sizeC = c.getBoundingClientRect();
// // console.log(sizeC)
//结论三:读后写,导致recalculate style && layout
// 读后写写,同理,但只要有写入都会触发recalculate style && layout
// a.getBoundingClientRect();
// a.style.width = "100px";
// b.style.width = "150px";
//结论四:写后读,会触发recalculate style && layout,因为前面有写入操作,所以需要重新计算style,再执行layout,再读取
// 和直接读取不同在于,直接读取时不需要重新计算style的
// 写后读读, 同理,但第二次读不需要重新计算,和上面连续读写的情况一样
// a.style.width = "100px";
// b.style.width = "150px";
// a.getBoundingClientRect();
// b.getBoundingClientRect();
//结论五:写读写读写读... 会不断触发recalculate style && layout
// a.style.width = "100px";
// a.getBoundingClientRect();
// b.style.width = "100px";
// b.getBoundingClientRect();
//一些建议或感悟
/*
* ①充分利用Chrome的timeline
* ②浏览器对于每一帧的渲染过程:执行js -> style -> layout -> paint -> composite(同样,在之后会写一篇文章来介绍,可以先看这个https://developers.google.com/web/fundamentals/performance/rendering/)
* ③结论看过就忘了,自己开timeline试试吧,或者先有个概念,日后遇见了再回来试试。
*
**/
})
}
</script>
</body>
</html>
实验结论
- 连续读取同一个元素的高宽(多次读取也一样)只会触发一次layout,底层应该调用了
getBoundingClientRect() - 连续读取不同元素的高宽,只会触发一次layout,底层在layout的时候应该算出了所有元素的位置,下次再读取(没有写入的前提下)不会触发新的layout
- 读后写,导致
recalculate style && layout。读后写写,同理,但只要有写入都会触发recalculate style && layout
- 写后读,会触发recalculate style && layout,因为前面有写入操作,所以需要重新计算style,再执行layout,再读取。和直接读取不同在于,直接读取时不需要重新计算style的。 写后读读, 同理,但第二次读不需要重新计算,和上面连续读写的情况一样
- 写读写读写读… 会不断触发recalculate style && layout
一些建议或感悟
- 充分利用Chrome的timeline
- 浏览器对于每一帧的渲染过程:执行js -> style -> layout -> paint -> composite(同样,在之后会写一篇文章来介绍,可以先看这个
- 上面是基本的理论和工具,算得上是建议的是这个:如果业务中必须要读写读写,可以将读后处理的结果存下来,然后再连续写,将读写读写变成读读写写只会触发一次recalculate style && layout(2016-11-25更新)。
- 读写innerHTML同样会触发recalculate style && layout,请小心。最好的方法就是,上线前使用timeline工具检查一遍自己写的页面。再安利一篇简单上手文章:前端性能优化利器:Chrome Timeline学习及实践
- 结论看过就忘了,自己开timeline试试吧,或者先有个概念,日后遇见了再回来试试。
else
好像很久没写blog了,最近看书学习比较多,湖泊不知江海之大也,虚心向上。
410



被折叠的 条评论
为什么被折叠?



