如果上图不能理解的话,就看下面这个图
需要知道的是:
从http请求回来开始,这个过程就并非一般想象中的一步做完再做下一步,而是一条流水线
从http请求回来,就产生了流式的数据,后续的DOM 树构建,css计算,渲染,合成,绘制,都是尽可能的流式的处理前一步的产出;即不需要等到上一步完全结束,就直接开始上一步的输出,这样我们在浏览网页时,才会看到逐步出现的页面。
我觉得这个部分比较像编译原理(通过词法分析,语法分析进行构建)
这颗构建的Dom 树,实际上信息不是全的,只有节点和属性,不包括任何的样式和信息
构建Dom 的过程是:从父到子,从先到后,一个一个节点构造,并且挂载到Dom 树上
在构建Dom 树时,同步计算了css 属性
我们依次拿到上一步构建好的元素,去检查他匹配了哪些规则,再根据规则的优先级,进行覆盖和调整
选择器的出现顺序,必定跟构建Dom 树的顺序一致。这是css设计的原则,即保证选择器在Dom 树构建到当前节点时,已经可以准确的判断是否可以匹配,而不需要后续节点的信息
可分为
可分为
渲染过程(特指我这里讲的)是不会把子元素绘制到渲染的位图上去,这样,当父子元素的相对位置发生变化时,可以保证渲染的结果能够最大程度化被缓存,减少重新渲染
上一小节中讲到,渲染过程不会把子元素渲染到位图上面,合成的过程:就是为一些元素创建一个“合成后的位图”(合成层),把一部分子元素渲染到合成的位图上面
目前主流浏览器一般根据position transform 等属性来决定合成策略,来“猜测”这些元素在未来可能发生变化
绘制是把“位图最终绘制到屏幕上,变成肉眼可见的图像”
我们把任何位图合成到这个“最终位图”的操作称为绘制
一般来说浏览器并不需要用代码来处理这个过程,浏览器只需要把最终的要显示的位图交给操作系统即可
在上面两个小节中,已经得到了每个元素的位图,并且对他们部分进行了合成,绘制过程就是按照z-index
把他们一次绘制到屏幕上
仔细理解概念:单单改变元素的外观,肯定不会引起网页重新生成布局。但是浏览器完成重排之后,重绘将会受到重排的影响。
"重绘"不一定会出现"重排","重排"必然会出现"重绘"
DOM对象的位置和尺寸大小改变时,浏览器需要重新计算元素的几何属性,将其安放在界面中的正确位置,这个过程叫做重排。
回流:
回流就好比向河里(文档流)扔了一块石头(dom变化),激起涟漪,然后引起周边水流受到波及,所以叫做回流
任何会改变元素几何信息(元素的位置和尺寸大小)的操作,都会触发重排
由于浏览器渲染界面是基于流失布局模型的,所以触发重排时会对周围DOM重新排列,影响的范围有两种:
html
开始对整个渲染树进行重新布局。当一个元素的外观发生改变,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘。
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
根据我们上文的定义,这段代码理论上会触发4次重排+重绘,因为每一次都改变了元素的几何属性,实际上最后只触发了一次重排,这都得益于浏览器的渲染队列机制:
当我们修改了元素的几何属性,导致浏览器触发重排或重绘时。它会把该操作放进渲染队列,等到队列中的操作到了一定的数量或者到了一定的时间间隔时,浏览器就会批量执行这些操作。
强制刷新队列:
div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);
这段代码会触发4次重排+重绘,因为在console中你请求的这几个样式信息,无论何时浏览器都会立即执行渲染队列的任务,即使该值与你操作中修改的值没关联。
因为队列中,可能会有影响到这些值的操作,为了给我们最精确的值,浏览器会立即重排+重绘。
强制刷新队列的style样式请求:
offsetTop, offsetLeft, offsetWidth, offsetHeight
scrollTop, scrollLeft, scrollWidth, scrollHeight
clientTop, clientLeft, clientWidth, clientHeight
getComputedStyle(), 或者 IE的 currentStyle
我们在开发中,应该谨慎的使用这些style请求,注意上下文关系,避免一行代码一个重排,这对性能是个巨大的消耗
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
console.log(div.offsetLeft);
console.log(div.offsetTop);
console.log(div.offsetWidth);
console.log(div.offsetHeight);
还是上面触发4次重排+重绘的代码,这次只触发了一次重排:
在第一个console的时候,浏览器把之前上面四个写操作的渲染队列都给清空了。剩下的console,因为渲染队列本来就是空的,所以并没有触发重排,仅仅拿值而已。
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
虽然现在大部分浏览器有渲染队列优化,不排除有些浏览器以及老版本的浏览器效率仍然低下:
建议通过改变class或者csstext属性集中改变样式
// bad
var left = 10;
var top = 10;
el.style.left = left + "px";
el.style.top = top + "px";
// good
el.className += " theclassname";
// good
el.style.cssText += "; left: " + left + "px; top: " + top + "px;";
// bad 强制刷新 触发两次重排
div.style.left = div.offsetLeft + 1 + 'px';
div.style.top = div.offsetTop + 1 + 'px';
// good 缓存布局信息 相当于读写分离
var curLeft = div.offsetLeft;
var curTop = div.offsetTop;
div.style.left = curLeft + 1 + 'px';
div.style.top = curTop + 1 + 'px';
dom.display = 'none'
// 修改dom样式
dom.display = 'block'
参考文章:https://juejin.im/post/5c15f797f265da61141c7f86