前言
复习浏览器原理这一块的浏览器渲染过程
浏览器渲染过程
这个过程是
- 解析HTML,生成Dom树
- 解析CSS,生成Style Rules
- 接着HTML和CSS进行结合,生成渲染树
Render Tree
- 之后通过计算每一个元素的大小,位置,给出每个节点所应该出现的屏幕精确坐标,从而得到基于渲染树的 布局渲染树 (
Layout of the render tree
)。 - 遍历渲染树,将每个节点用
UI
渲染引擎来绘制,从而将整棵树绘制到页面上,这个步骤叫 绘制渲染树 (Painting the render tree
)
重绘
元素样式发生改变,并不涉及到位置发生改变。这个影响的是上面的步骤五
会引起重绘的: color、background-color、visibility
回流(重排)
元素的尺寸、结构、位置发生了改变会引起回流。涉及一些width、height、padding、margin、left、top之类的会触发回流。
引起回流的:
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小等等)
- 元素字体大小变化
- 添加或者删除可见 的
DOM
元素 - 激活
CSS
伪类(例如::hover
) - 查询某些属性或调用某些方法
注意
-
回流的代价比重绘的要大,当一个元素的位置发生了改变,旁边的元素可能会相应地位置发生改变,但是当一个元素的样式发生改变,不会引起其他元素样式的改变。
-
但是现在浏览器对这个进行了优化: 浏览器会把所有的重绘和重排这些任务放到一个队列里面,等到了一定的时间或者数量的话就会对这些任务进行批量处理。这样就大大减少了重绘和重排的次数。
ini
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';
这种只需要触发一次重排
- 当遇到
offsetWidth、offsetHeight、clientWidth、clientHeight、scrollTop、scrollWidth
这些的时候,因为需要及时计算出来的,所以浏览器需要重新进行布局计算,所以之前的队列里面的任务将会清空。
ini
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次重排+重绘
降低重绘和回流的方法
CSS:
- 避免使用Table布局
- 避免设置多层内联样式
- 避免使用
CSS
表达式(例如:calc()
) - 使用
visibility
替换display
JS:
- 使用类名对样式逐条更改
ini
const div = document.querySelector('.myDiv')
div.style.width = '100px'
div.style.height = '100px'
div.style.border = '1px solid blue'
这样会多次触发重绘和重排,应该
css
.myDiv{
width:100px;
height:100px;
border:1px solid blue;
}
duv.classList.add('myDiv')
- 缓存对敏感属性值的计算
有些场景我们需要多次计算来获取元素在页面的布局位置
ini
const list = document.querySelector('.list')
for(let i = 0;i < 10;i++){
list.style.top = `${list.offsetTop + 10}px`
list.style.left = `${list.offsetLeft + 10}px`
}
这个计算属性会导致触发布局重新计算,所以消耗的性能会很大,需要对它进行缓存
ini
const list = document.querySelector('.list')
let offsetTop = list.offsetTop,offsetLeft = list.offsetLeft
for(let i = 0;i < 10;i++){
offsetTop += 10
offsetLeft += 10
}
list.style.top = offsetTop
list.style.left = offsetLeft
- 也可以先为元素设置
display: none
,操作结束后再把它显示出来。因为在display
属性为none
的元素上进行的DOM
操作不会引发回流和重绘。
ini
dom.display = 'none'
// 修改dom样式
dom.display = 'block'
4.position属性为absolute或fixed:重排开销比较小,不用考虑它对其他元素的影响
最后
感谢观看