857
社区成员
这是我参加朝闻道知识分享大赛的第18篇文章。
注:CSS面试题分享板块的内容均为常见的前端面试中会涉及的知识点。
具体的问题内容大同小异,大家遇到时具体问题具体分析即可。
前言:在我们对页面的元素进行修改时,常常会触发回流和重绘。少量的回流会重绘是不可避免的,但是我们需要注意我们的一些循环操作会导致大量的回流和重绘,将会影响浏览器的性能。本文介绍了页面渲染的流程以及回流和重绘的触发机制。了解这些机制,并学会使用一些常见的方法去减少回流和重绘,可以让我们的网页具有更好的性能。
目录
在HTML中,每个元素都可以理解为一个盒子,在浏览器解析元素时,就会涉及到回流与重绘。
回流:计算盒子的几何信息(大小和位置)。
重绘:根据每个盒子的特性进行绘制。
浏览器渲染页面的具体流程:
解析HTM和CSS,生成DOM树、CSSOM树
DOM树、CSSOM树结合,生成Render树
Layout(回流):根据Render树,回流得到节点的位置和大小
Painting(重绘):根据Render树和回流得到的节点的几何信息绘制具体的节点的绝对像素
Display(显示):把重绘获得的绝对像素发送到GPU,在页面上进行展示
综上,在页面最开始加载的过程中,回流和重绘是不可避免的会触发一次的。
当我们修改元素的几何信息时,就会触发回流与重绘;
当我们修改元素的特征信息时,只会触发重绘。
我们前面提到,在几何信息被修改时,会触发回流,具体有以下几种行为:
页面第一次渲染
可见的DOM元素被删除
元素的内容发生了变化
元素的大小、位置发生了变化(包括边距、边框)
浏览器的窗口尺寸发生了变化(回流根据视口大小计算元素的几何信息)
特殊的,在使用getComputedStyle
方法或offset
/scroll
/client
等属性时,由于这些方法/属性需要即使获取页面的几何信息,此时浏览器也会发生回流为我们获取这些值。
所有会触发回流的,一定会触发重绘。以下是会单独引起重绘的行为:
修改颜色
修改阴影
修改文本方向
我们常用的优化方式如下:
修改多个样式时,使用修改元素类名的方式,避免在js
中直接修改样式
插入多个节点时,使用DocumentFragment
一次性插入多个创建好的节点
避免使用table
布局,因为table
中每个元素的变动都会导致整个table
的重新计算
给复杂的动画设置position: fixed/absolute
,让元素脱离文档流,减少对其他元素的影响
由于CSS3
的硬件加速,我们可以多使用transform
、opacity
、filters
,这些动画不会引起回流和重绘。
示例1:
优化前:每次单独操作都会触发一次渲染树的更改(新版本的浏览器中已优化)
const container = document.getElementById("container")
container.style.width = "100px"
container.style.height = "200px"
container.style.border = "10px solid red"
container.style.color = "red"
优化方法一:添加类名,一次性添加所有属性
<style>
.basic_style {
width: 100px;
height: 200px;
border: 10px solid red;
color: red;
}
</style>
<script>
const container = document.getElementById("container")
container.classList.add("basic_style")
</script>
优化方法二:离线操作,用display: none;
去除元素后再进行操作
let container = document.getElementById('container')
container.style.display = 'none'
container.style.width = '100px'
container.style.height = '200px'
container.style.border = '10px solid red'
container.style.color = 'red'
container.style.display = 'block'
示例2:
优化前:在循环中多次获取offset
属性,且多次触发渲染树渲染,会导致页面多次计算,发生多次回流。
const el = document.getElementById("el")
for (let i = 0; i < 10; i++) {
el.style.top = el.offsetTop + 10 + "px"
el.style.left = el.offsetLeft + 10 + "px"
}
优化后:使用变量缓存需要的值,只获取一次// 缓存offset变量
const el = document.getElementById('el')
let offLeft = el.offsetLeft, offTop = el.offsetTop
// 在JS中对总的偏移量进行计算
for(let i=0;i<10;i++) {
offLeft += 10
offTop += 10
}
// 在DOM中修改偏移量
el.style.left = offLeft + "px"
el.style.top = offTop + "px"