前端开发之浏览器相关
一、关于重绘和回流
回流
前面我们通过构造渲染树,我们将可见DOM节点以及它对应的样式结合起来,可是我们还需要计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流。
为了弄清每个对象在网站上的确切大小和位置,浏览器从渲染树的根节点开始遍历,我们可以以下面这个实例来表示:
<html><<span se-mark="1"> <<span se-mark="1">head> <<span se-mark="1">meta name="viewport" content="width=device-width,initial-scale=1"> <<span se-mark="1">title>Critial Path: Hello world!</<span se-mark="1">title> </<span se-mark="1">head> <<span se-mark="1">body> <<span se-mark="1">div style="width: 50%"> <<span se-mark="1">div style="width: 50%">Hello world!</<span se-mark="1">div> </<span se-mark="1">div> </<span se-mark="1">body></<span se-mark="1"></html>
我们可以看到,第一个div将节点的显示尺寸设置为视口宽度的50%,第二个div将其尺寸设置为父节点的50%。而在回流这个阶段,我们就需要根据视口具体的宽度,将其转为实际的像素值。(如下图)
重绘
最终,我们通过构造渲染树和回流阶段,我们知道了哪些节点是可见的,以及可见节点的样式和具体的几何信息(位置、大小),那么我们就可以将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段就叫做重绘节点。
既然知道了浏览器的渲染过程后,我们就来探讨下,何时会发生回流重绘。
何时发生回流重绘
我们前面知道了,回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流。比如以下情况:
- 添加或删除可见的DOM元素
- 元素的位置发生变化
- 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
- 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
- 页面一开始渲染的时候(这肯定避免不了)
- 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
注意:回流一定会触发重绘,而重绘不一定会回流
根据改变的范围和程度,渲染树中或大或小的部分需要重新计算,有些改变会触发整个页面的重排,比如,滚动条出现的时候或者修改了根节点。
详细查看:https://segmentfault.com/a/1190000017329980
二、关于浏览器优化机制
浏览器的优化机制
现代的浏览器都是很聪明的,由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列。但是!当你获取布局信息的操作的时候,会强制队列刷新,比如当你访问以下属性或者使用以下方法:
offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
getComputedStyle()
getBoundingClientRect
- 具体可以访问这个网站:https://gist.github.com/pauli...
以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发回流重绘来返回正确的值。因此,我们在修改样式的时候,最好避免使用上面列出的属性,他们都会刷新渲染队列。如果要使用它们,最好将值缓存起来。
代码是从上往下依次执行的解释的,很多时候我们发现代码并没有按我们想象的来执行,比如:
var $content = $element.find('.infoJS').removeClass('move-a-show');
//console.log($content);
//调整位置
$content.css(DIR_POS[dir]);
//reflow (触发回流重绘来更新这个元素的最新布局信息)
$content[0].offsetWidth; //(offsetWidth会强制队列刷新)
//启用过渡
$content.addClass('move-a-show');
//滑入
$content.css({left: '0', top: '0'});
这里正常逻辑是先执行完$content.css(DIR_POS[dir]);之后在执行$content.css({left: '0', top: '0'});,但是页面上的效果并不是先显示$content.css(DIR_POS[dir]);的执行结果在显示$content.css({left: '0', top: '0'});的执行结果,而是是直接显示了$content.css({left: '0', top: '0'});的执行结果;所以会造成一些问题!原因是浏览器的优化机制:如果执行结果涉及到重新渲染,重绘,重排的过程,那么大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才会清空,刷新队列,所以上面这段代码最终是批量一起执行渲染该元素,导致页面上看到的只有队列最后渲染的结果,这里如果需要在某一列执行的时候强制钙元素渲染可以用上面介绍的一些元素,比如:$content[0].offsetWidth; 这行代码就会触发改元素回流重绘来返回正确 最新的值!
浙公网安备 33010602011771号