介绍回流与重绘(Reflow & Repaint),以及如何进行优化?
前言
回流与重绘对于前端来说可以说是非常重要的知识点了,我们不仅需要知道什么是回流与重绘,还需要知道如何进行优化。一个页面从加载到完成,首先是构建DOM树,然后根据DOM节点的几何属性形成render树(渲染树),当渲染树构建完成,页面就根据DOM树开始布局了,渲染树也根据设置的样式对应的渲染这些节点。在这个过程中,回流与DOM树,渲染树有关,重绘与渲染树有关。
如果这篇文章有帮助到你,️关注+点赞️鼓励一下作者,文章公众号首发,关注 前端南玖
第一时间获取最新的文章~
页面渲染过程
- 解析HTML构建DOM Tree
- 解析CSS构建CSSOM Tree
- 构建渲染树(Render Tree),渲染树只包含渲染网页所需的节点
为构建渲染树,浏览器大体上完成了下列工作:
- 从 DOM 树的根节点开始遍历每个可见节点。
- 某些节点不可见(例如脚本标记、元标记等),因为它们不会体现在渲染输出中,所以会被忽略。
- 某些节点通过 CSS 隐藏(例如display: none),因此在渲染树中也会被忽略。
- 对于每个可见节点,为其找到适配的 CSSOM 规则并应用它们。
- 发射可见节点,连同其内容和计算的样式。
Note: 请注意 visibility: hidden
与 display: none
是不一样的。前者隐藏元素,但元素仍占据着布局空间(即将其渲染成一个空框),而后者 (display: none
) 将元素从渲染树中完全移除,元素既不可见,也不是布局的组成部分。
最终输出的渲染同时包含了屏幕上的所有可见内容及其样式信息。有了渲染树,我们就可以进入“布局”阶段。
- 布局计算每个DOM对象的精确位置和大小
- 渲染(绘制,合成),使用最终渲染树将像素渲染到屏幕上
有关页面渲染的过程可以看我之前的文章:超详细讲解页面加载过程,这里我们把重点放在重绘与回流上。
什么是回流(Reflow)与重绘(Repaint)?
回流(Reflow)
当渲染树render tree
中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建render tree
。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程称为重绘。
简单来说,回流就是计算元素在设备内的确切位置和大小并且重新绘制
回流的代价要远大于重绘。并且回流必然会造成重绘,但重绘不一定会造成回流。
重绘(Repaint)
当渲染树render tree
中的一些元素需要更新样式,但这些样式属性只是改变元素的外观,风格,而不会影响布局的,比如background-color
。则就叫称为重绘(repaint)。
简单来说,重绘就是将渲染树节点转换为屏幕上的实际像素,不涉及重新布局阶段的位置与大小计算
为什么不建议频繁操作DOM?
我们都知道操作DOM其实是非常耗性能的,所以我们不仅要避免去操作DOM,还要减少访问DOM的次数。
因为在浏览器中,DOM
和JS
的实现,并不是在同一个引擎中完成的。DOM
是属于渲染引擎
中的东⻄,⽽JS
⼜是JS引擎
中的东⻄。当我们通过JS
操作DOM
的时候,就涉及到了两个线程之间的通信,那么势必会带来⼀些性能上的损耗。操作DOM次数⼀多,也就等同于⼀直在进⾏线程之间的通信,并且操作DOM可能还会带来重绘回流的情况,所以也就导致了性能上的问题。
把DOM和JavaScript各自想象成一个岛屿,它们之间用收费桥梁连接。
--《高性能JavaScript》
何时会发生回流(Reflow)与重绘(Repaint)?
会导致回流的操作:
- 页面首次渲染(无法避免且开销最大的一次)
- 浏览器窗口大小发生改变(resize事件)
- 元素尺寸或位置发生改变(边距、宽高、边框等)
- 元素内容变化(文字数量或图片大小等等)
- 元素字体大小变化(font-size)
- 添加或者删除可见的
DOM
元素 - 激活
CSS
伪类(例如::hover
) - 查询某些属性或调用某些方法
一些常用且会导致回流的属性和方法:
引起回流属性和方法 | -- | -- | -- |
---|---|---|---|
width | height | margin | padding |
display | border-width | border | position |
overflow | font-size | vertical-align | min-height |
clientWidth | clientHeight | clientTop | clientLeft |
offsetWidth | offsetHeight | offsetTop | offsetLeft |
scrollWidth | scrollHeight | scrollTop | scrollLeft |
scrollIntoView() | scrollTo() | getComputedStyle() | |
getBoundingClientRect() | scrollIntoViewIfNeeded() |
为什么获取一些属性或调用方法也会导致回流?
因为以上属性和方法都需要返回最新的布局信息,因此浏览器不得不触发回流重绘来返回正确的值。
会导致重绘的属性
属性: | -- | -- | -- |
---|---|---|---|
color | border-style | visibility | background |
text-decoration | background-image | background-position | background-repeat |
outline-color | outline | outline-style | border-radius |
outline-width | box-shadow | background-size |
具体可以在这个网站查找CSS Triggers
浏览器的优化机制
由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才会进行批量修改并清空队列。但是,在获取布局信息的时候,会强制刷新队列,比如当你访问以下属性或者使用以下方法:
clientTop、clientLeft、clientWidth、clientHeight
offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
getComputedStyle()
getBoundingClientRect
具体可以访问这个网站:paulirish
以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发回流重绘来返回正确的值。因此,我们在修改样式的时候,最好避免使用上面列出的属性,他们都会刷新渲染队列。
如何减少回流(Reflow)与重绘(Repaint)?(优化)
合并对DOM
样式的修改,采用css class
来修改
const el = document.querySelector('.box')
el.style.margin = '5px'
el.style.borderRadius = '12px'
el.style.boxShadow = '1px 3px 4px #ccc'
建议使用css class
.update{
margin: 5px;
border-dadius: 12px;
box-shadow: 1px 3px 4px #ccc
}
const el = document.querySelector('.box')
el.classList.add('update')
如果需要对DOM进行多次访问,尽量使用局部变量缓存该DOM
避免使用table布局,可能很⼩的⼀个⼩改动会造成整个table的重新布局
CSS选择符从右往左匹配查找,避免节点层级过多
DOM离线处理,减少回流重绘次数
离线的DOM不属于当前DOM树中的任何一部分,这也就意味着我们对离线DOM处理就不会引起页面的回流与重绘。
- 使用
display: none
,上面我们说到了 (display: none
) 将元素从渲染树中完全移除,元素既不可见,也不是布局的组成部分,之后在该DOM上的操作不会触发回流与重绘,操作完之后再将display
属性改为显示,只会触发这一次回流与重绘。
提醒:visibility : hidden
的元素只对重绘有影响,不影响重排。
- 通过 documentFragment 创建一个
dom
文档片段,在它上面批量操作dom
,操作完成之后,再添加到文档中,这样只会触发一次重排。
const el = document.querySelector('.box')
const fruits = ['front', 'nanjiu', 'study', 'code'];
const fragment = document.createDocumentFragment();
fruits.forEach(item => {
const li = document.createElement('li');
li.innerHTML = item;
fragment.appendChild(li);
});
el.appendChild(fragment);
- 克隆节点,修改完再替换原始节点
const el = document.querySelector('.box')
const fruits = ['front', 'nanjiu', 'study', 'code'];
const cloneEl = el.cloneNode(true)
fruits.forEach(item => {
const li = document.createElement('li');
li.innerHTML = item;
cloneEl.appendChild(li);
});
el.parentElement.replaceChild(cloneEl,el)
DOM脱离普通文档流
使用absoult
或fixed
让元素脱离普通文档流,使用绝对定位会使的该元素单独成为渲染树中 body
的一个子元素,重排开销比较小,不会对其它节点造成太多影响。
CSS3硬件加速(GPU加速)
使用css3硬件加速,可以让transform、opacity、filters
这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color
这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。
常见的触发硬件加速的css属性:
- transform
- opacity
- filters
- Will-change
将节点设置为图层
图层能够阻⽌该节点的渲染⾏为影响别的节点。⽐如对于video标签来说,浏览器会⾃动将该节点变为图层。
推荐阅读
- Promise、Generator、Async有什么区别?
- 【Vue源码学习】依赖收集
- 【Vue源码学习】响应式原理探秘
- JS定时器执行不可靠的原因及解决方案
- 从如何使用到如何实现一个Promise
- 超详细讲解页面加载过程
原文首发地址点这里,欢迎大家关注公众号 「前端南玖」,回复进群,拉你进前端交流群一起学习,回复资料,领取前端电子书和学习视频~。
我是南玖,我们下期见!!!
介绍回流与重绘(Reflow & Repaint),以及如何进行优化?的更多相关文章
- 浏览器的回流与重绘 (Reflow & Repaint)
写在前面 在讨论回流与重绘之前,我们要知道: 浏览器使用流式布局模型 (Flow Based Layout). 浏览器会把HTML解析成DOM,把CSS解析成CSSOM,DOM和CSSOM合并就产生了 ...
- 回流(reflow)与重绘(repaint)
最近项目排期不紧,于是看了一下之前看了好久也没看明白的chrome调试工具的timeline.但是很遗憾,虽然大概懂了每一项是做什么的,但是用起来并不能得心应手.所以今天的重点不是timeline,而 ...
- 重绘(Repaint)和回流(Reflow)
重绘(Repaint)和回流(Reflow) 1.回流和重绘只是渲染步骤的一小节,是怎么做到影响性能的? css 会影响 javascrip 执行时间导致 javascript 脚本变慢 浏览器渲染一 ...
- 什么是回流(重排 reflow)?什么是重绘(repaint)?如何减少回流、重绘?
什么是回流(重排 reflow)? 回流(重排 reflow):对DOM树进行渲染,只要修改DOM或修改元素的形状大小,就会触发reflow,reflow的时候,浏览器会使已渲染好受到影响的部分失效, ...
- 【JavaScript】回流(reflow)与重绘(repaint)
重绘与回流 首先要了解页面是如何呈现的: HTML文档加载后生成DOM树(包括display:none;元素): 在DOM树的基础上配合css样式结构体生成render树(不包含display:non ...
- HTML页面的重绘(repaint)和重流(reflow)
重流(Reflow)是指布局引擎为frame计算图形的过程. frame是一个矩形,拥有宽高和相对父容器的偏移.frame用来显示盒模型(content model), 但一个content mode ...
- 【笔记】web 的回流与重绘及优化
最近看了幕课网 web 前端性能优化的课程,其中说到了浏览器的回流(reflow) 及 重绘(repaint).觉得以后面试或许会被问到所以做一下笔记: 课程从回流及重绘这两个点延伸出了一个知识点就是 ...
- DOM的回流和重绘(重排、重绘)
什么是DOM回流? 页面渲染时,我们对HTML结构简单的增删查改时,浏览器会对所有的dom进行重新排序,这就i是DOM回流,严重影响浏览器性能 DOM的回流和重绘: **DOM的回流**:当页面中元素 ...
- JS学习笔记:(二)回流和重绘
在搞清楚回流和重绘的概念之前,我们要清除浏览器的渲染过程. 解析生成DOM Tree(此时包含所有节点,包括display:none); 根据CSS Object Module(CCSSOM)计算节点 ...
随机推荐
- CAP 6.0 版本发布通告 - 支持 OpenTelemetry
前言 今天,我们很高兴宣布 CAP 发布 6.0 版本正式版,在这个版本中,我们主要致力于对 OpenTelemetry 提供支持,以及更好的适配 .NET 6. 那么,接下来我们具体看一下吧. 总览 ...
- django中的时区问题
在django中设置时区,通过setting文件中的: TIME_ZONE = 'Asia/Shanghai' 开起多时区支持功能:USE_TZ=True 这时在数据库中插入的时间为UTC时间,当调用 ...
- [服务器部署] Flask + virtualenv + uWSGI + Nginx 遇到的问题
1.配置好了Flask + virtualenv +uWSGI,启动uWSGI并调试,网页显示 Internal Server Error 参考:https://www.cnblogs.com/cle ...
- POJ prime distance
https://oj.shiyancang.cn/Problem/781.html 素数距离,数据范围21亿,如果用素数筛存,并且进行做的话,按照x/lnx计算会是一个非常恐怖的复杂度.确定要做什么, ...
- SYCOJ137斜线输出(1)
题目-斜线输出(1) (shiyancang.cn) 在同一斜线上的满足方程.坐标关系计算即可. #include<bits/stdc++.h> using namespace std; ...
- 对飞猪H5端API接口sign签名逆向实验
免责声明 本文章所提到的技术仅用于学习用途,禁止使用本文章的任何技术进行发起网络攻击.非法利用等网络犯罪行为,一切信息禁止用于任何非法用途.若读者利用文章所提到的技术实施违法犯罪行为,其责任一概由读者 ...
- 用js判断页面是否加载完成实现代码
方式一:window.onload: 当一个文档完全下载到浏览器中时,才会触发window.onload事件.这意味着页面上的全部元素对js而言都是可以操作的,也就是说页面上的所有元素加载完毕才会执行 ...
- 热词cloud-EChart安装
1.安装npm install echarts npm install echarts-wordcloud注意版本:echarts版本5只能和wordcloud版本2的一起使用 :echarts版本4 ...
- Cesium入门4 - 创建Cesium Viewer
Cesium入门4 - 创建Cesium Viewer Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 任何Ce ...
- 事务与一致性:刚性or柔性
转发自 https://cloud.tencent.com/developer/article/1038871 在高并发场景下,分布式储存和处理已经是常用手段.但分布式的结构势必会带来"不一 ...