We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
为了构建渲染树,浏览器主要完成以下几个工作:
注意的是:渲染树只包含可见节点
浏览器将DOM树和CSSOM树组合成渲染树之后,还需要计算节点在设备视口内的确切位置和大小,这个计算的阶段就是回流。
为弄清每个节点在网页上的确切大小和位置,浏览器从渲染树的根节点开始进行遍历。我们来看一下例子:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>Critial Path: Hello world!</title> </head> <body> <div style="width: 50%"> <div style="width: 50%">Hello world!</div> </div> </body> </html>
我们可以看到第一个div将节点的显示尺寸设置为视口宽度的50%,父div包含的第二个div将其宽度设置为其父节点宽度的50%,即视口的25%。而在回流阶段,我们就需要根据视口宽度,将其转为具体的像素值。
既然我们知道了哪些可见节点,它们的计算样式以及几何信息,我们终于可以将这些信息传递给最后一个阶段,将渲染树上的每个节点转换成屏幕上的实际像素。这一步通常被称为:重绘。
我们了解到,回流阶段主要是计算节点的位置或几何信息,那么当页面布局和几何信息发生变化的时候,就需要进行回流。比如:
这里需要注意的是:回流一定会导致重绘,但是重绘不一定会导致回流
当我们获取DOM节点的布局信息时,也会导致浏览器回流重绘。比如:
可以访问这里去查看:
当我们获取节点的这些属性值时,都会导致浏览器回流重绘,因为当我们获取这些值的时候,需要返回最新的值,所以浏览器会触发回流,重新计算布局信息,来返回正确的值。
let box = document.getElementById('box'); box.style.padding = '10px'; box.style.borderTop = '20px'; box.style.width = '100px';
向上面的例子中,每次重新设置节点的样式都会导致浏览器回流重绘,所以最好是将修改样式的代码合并成一句,比如:
box.style.cssText = 'padding:10px;borderTop : 20px;width:100px';
或者也可以通过添加一个样式类,来达到这样的效果。
当我们需要对DOM进行一系列修改的时候,我们可以这样来减少回流重绘次数:使元素脱离文档流,然后对其进行多次修改,最后将元素放回到文档中。
这里有三种方式可以使DOM脱离文档流:
如果我们需要通过循环,批量插入节点到文档中,一般的做法都是:
let list = document.getElementById('list'); let data = [1,2,3,4,5,6,7,8,9,10]; let appendToList = function (target , data) { for (let i = 0 ; i < data.length ; i++) { let li = document.createElement('li'); li.innerHTML = data[i]; target.appendChild(li); } }; appendToList(list , data);
但是每一次插入节点的时候都会引起浏览器回流重绘,所以我们可以使用这三种方式来进行优化:
let list = document.getElementById('list'); list.style.display = 'none'; let data = [1,2,3,4,5,6,7,8,9,10]; let appendToList = function (target , data) { for (let i = 0 ; i < data.length ; i++) { let li = document.createElement('li'); li.innerHTML = data[i]; target.appendChild(li); } }; appendToList(list , data); list.style.display = 'block';
let list = document.getElementById('list'); let data = [1,2,3,4,5,6,7,8,9,10]; let appendToList = function (target , data) { for (let i = 0 ; i < data.length ; i++) { let li = document.createElement('li'); li.innerHTML = data[i]; target.appendChild(li); } }; let fragment = document.createDocumentFragment(); appendToList(fragment , data); list.appendChild(fragment);
let list = document.getElementById('list'); let data = [1,2,3,4,5,6,7,8,9,10]; let appendToList = function (target , data) { for (let i = 0 ; i < data.length ; i++) { let li = document.createElement('li'); li.innerHTML = data[i]; target.appendChild(li); } }; let clone = list.cloneNode(true); appendToList(clone , data); list.parentNode.replaceChild(clone , list);
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> #box { background: red; animation: scale 4s linear 0s infinite alternate; background-image: linear-gradient(to right, rgba( 0,0,0,0.9 ) 25%, rgba( 0,0,0,0.1 ) 50%, rgba( 0,0,0,0.9 ) 75%); will-change: all; transform: translate3d(0, 0, 0); } @keyframes scale { from { width: 100px; height: 100px; background: red; margin: 10px; transform: rotate(0); margin-left: -20%; rotate: 10deg; } to { width: 200px; height: 200px; background: yellow; margin: 50px; transform: rotate(360deg); margin-left: 100%; } } </style> </head> <body> <div id="box"></div> <button id="btn">click</button> <p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p> <p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p> <p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p> <p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p> <p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p> <p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p><p>12</p> <script> let box = document.getElementById('box'); btn.addEventListener('click' , function () { box.style.position = 'absolute'; }); let frame = 0; let start = null; function loop (timestamp) { if (!start) { start = timestamp; } frame++; let diff = timestamp - start; if (diff > 1000) { console.log('1秒内刷新频率为:' + Math.round((frame * 1000) / diff)); } requestAnimationFrame(loop); } requestAnimationFrame(loop) </script> </body> </html>
The text was updated successfully, but these errors were encountered:
No branches or pull requests
浏览器渲染过程
为了构建渲染树,浏览器主要完成以下几个工作:
注意的是:渲染树只包含可见节点
回流
浏览器将DOM树和CSSOM树组合成渲染树之后,还需要计算节点在设备视口内的确切位置和大小,这个计算的阶段就是回流。
为弄清每个节点在网页上的确切大小和位置,浏览器从渲染树的根节点开始进行遍历。我们来看一下例子:
我们可以看到第一个div将节点的显示尺寸设置为视口宽度的50%,父div包含的第二个div将其宽度设置为其父节点宽度的50%,即视口的25%。而在回流阶段,我们就需要根据视口宽度,将其转为具体的像素值。
重绘
既然我们知道了哪些可见节点,它们的计算样式以及几何信息,我们终于可以将这些信息传递给最后一个阶段,将渲染树上的每个节点转换成屏幕上的实际像素。这一步通常被称为:重绘。
什么时候会发送回流重绘
我们了解到,回流阶段主要是计算节点的位置或几何信息,那么当页面布局和几何信息发生变化的时候,就需要进行回流。比如:
这里需要注意的是:回流一定会导致重绘,但是重绘不一定会导致回流
当我们获取DOM节点的布局信息时,也会导致浏览器回流重绘。比如:
可以访问这里去查看:
当我们获取节点的这些属性值时,都会导致浏览器回流重绘,因为当我们获取这些值的时候,需要返回最新的值,所以浏览器会触发回流,重新计算布局信息,来返回正确的值。
减少回流和重绘
1、合并多次对DOM样式的修改
向上面的例子中,每次重新设置节点的样式都会导致浏览器回流重绘,所以最好是将修改样式的代码合并成一句,比如:
或者也可以通过添加一个样式类,来达到这样的效果。
批量修改DOM
当我们需要对DOM进行一系列修改的时候,我们可以这样来减少回流重绘次数:使元素脱离文档流,然后对其进行多次修改,最后将元素放回到文档中。
这里有三种方式可以使DOM脱离文档流:
如果我们需要通过循环,批量插入节点到文档中,一般的做法都是:
但是每一次插入节点的时候都会引起浏览器回流重绘,所以我们可以使用这三种方式来进行优化:
隐藏元素
使用文档片段
将原始元素拷贝到一个脱离文档的节点中
对于一些复杂的动画,可以使用绝对定位使其脱离文档流
The text was updated successfully, but these errors were encountered: