杭州下雪了,冷到不行,在家躺在床上玩手机,打开微信进入前端交流群里平常吹水,看到大佬在群里发了一篇文章你应该要知道的重绘与重排,文章里有一段骚操做,就是为了减小重绘与重排,合并样式操做,这个骚操做成功的引发了个人注意,而后开启了个人探索。css
前言中描述的合并样式的骚操做是以下:html
var el = document.querySelector('div'); el.style.borderLeft = '1px'; el.style.borderRight = '2px'; el.style.padding = '5px';
原文描述的大概意思是这段代码屡次对 DOM 的修改和对样式的修改,页面会进行屡次回流或者重绘,应该进行以下优化:前端
var el = document.querySelector('div'); el.style.cssText = 'border-left: 1px; border-right: 1px; padding: 5px;'
这样的优化在之前我刚开始学习前端的时候,常常也在一些相关的性能优化的文章里看到,由于一直没有探究过,概念里一直以为本身应该把屡次 DOM 的样式的修改合并在一块儿,这样效率会更高,直到后来,本身对浏览器的进程与线程慢慢有了了解,曾经也写过一篇博客,浅谈浏览器多进程与JS线程,其中有一个概念是,JS线程与GUI渲染线程是互斥关系
,大概的意思就是当js引擎在执行js代码的时候,浏览器的渲染引擎是被冻结了的,没法渲染页面的,必须等待js引擎空闲了才能渲染页面。chrome
这个概念,JS线程与GUI渲染线程是互斥关系
与上面描述的骚操做彷佛有点冲突,也就是当咱们对el.style
进行一系列赋值的时候,渲染引擎是被冻结的状态,怎么会进行屡次重绘或者回流?带着这样的疑问,写了一个小demo,代码以下。canvas
<!DOCTYPE html> <html> <head> <title>测试页</title> <style> #box { width: 109px; height: 100px; background-color: lightsteelblue; border-style: solid; } </style> </head> <body> <div id="box"></div> </body> <script> var box = document.getElementById('box'); var toggle = 0; var time = 500; function toggleFun() { var borderWidth = toggle ? 20 : 0; var borderColor = toggle ? 'coral' : 'transparent'; if (toggle) { box.style.borderWidth = '50px'; box.style.borderWidth = borderWidth + 'px'; box.style.borderColor = borderColor; } else { box.style.cssText = 'border: ' + borderWidth + 'px solid' + borderColor; } toggle = toggle ? 0 : 1; } setInterval(toggleFun, time) </script> </html>
代码大概的意思就是定时以两种操做设置样式,收集浏览器的回流或者重绘次数。segmentfault
打开chrome的开发者工具,切换到Performance
选项卡,点击左上角的圆 ○,开始record
,等几秒后stop
,点击Frames
查看Event log
选项卡,内容以下:浏览器
大概能够看到,Recalculate Style -> Layout -> Update Layer Tree -> Paint -> Composite Layers
这个过程在循环进行,触发的目标代码是第25行代码合29行代码,也就是box.style.borderWidth = '50px';
和box.style.cssText = 'border: ' + borderWidth + 'px solid' + borderColor;
。性能优化
首先回顾一下浏览器渲染页面的流程:微信
而后在看看上面的过程,能够容易看出,工具
Recalculate Style
,从新计算css规则树。Layout
,这里的Layout能够理解成回流,从新计算每一个元素的位置。Update Layer Tree
,字面意思理解,更新层级树。Paint
,绘制页面,在这里能够理解成重绘。Composite Layers
,字面意思理解,合并层级。由上面过程获得结果,当在同一执行任务里面对DOM的样式进行屡次操做的时候,只会进行一次回流或者重绘,也就是说,只要咱们的js引擎时候忙碌的,渲染引擎是冻结的时候,不管对DOM样式进行多少次操做,都只会进行一次回流或者重绘,也就是说前面说的合并样式
优化是无效的。
这个时候,我对上面过程又产生了新的疑问,为何要Paint
以后在Composite Layers
呢?为何不把全部层合并完了在绘制页面呢?
.........................(看搜索相关资料去了)
翻看资料结束后,我获得如下理解。
首先理解layer
概念,能够理解成PS里面的图层,咱们知道PS文件最后保存层PSD文件,当图层越多的时候,PSD文件就越大,在咱们的浏览器里面也是同样的,咱们的layer越多,所占的内存就越大。
而后理解Paint
真正作的事情,paint的任务大概就是把全部的layer绘制到页面中,这个绘制与canvas的绘制不同,canvas的绘制至关于在画布里把像素直接绘制成指定颜色,而后咱们直接看到的东西就直接是像素颜色,而咱们这里说的Paint
只是把图层丢到页面中,最后的绘制,须要交给Composite线程
处理。
最后是Composite Layers
,由composite线程
进行,这个线程在浏览器的Renderer进程中,任务是把Paint时候丢上页面的图层转化成位图,最终生成咱们肉眼能够看到的图像,因此,真正的绘制,应该是Composite Layers
过程进行的。
因为paint
与composite
解耦,浏览器对每个layer
都有一个标识,这个标识用来标识该layer
是否须要重绘,在有CSS规则树变化的时候,浏览器只会对这些被标识的layer进行重绘,用这样的方式提升浏览器的渲染性能。
前端大法博大精深,越往下学越以为本身不适合前端!!!仿佛看到本身在从入门到跑路这条路上快走到了终点。。。