写点什么

HTML 回流与重绘

用户头像
空城机
关注
发布于: 2021 年 05 月 13 日
HTML回流与重绘

什么是回流

指的是浏览器为了重新渲染页面的需要而进行的重新计算元素的几何大小和位置,他的开销是非常大的,回流可以理解为渲染树需要重新进行计算,一般最好触发元素的重构,避免元素的回流;比如通过添加 class 来添加 css 样式,而不是直接在 DOM 上设置,当需要操作某一块元素的时候,最好使其脱离文档流,这样就不会引起回流了,比如设置 position:absolute 或者 fixed,或者 display:none,等操作结束后在显示。

回流(Reflow)是指布局引擎为 frame 计算图形的过程。 frame 是一个矩形,拥有宽高和相对父容器的偏移。frame 用来显示盒模型(content model), 但一个 content model 可能会显示为多个 frame,比如换行的文本每行都会显示为一个 frame。

当 render tree 中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。

什么是重绘

当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如 background-color。则就叫称为重绘。


回流必将引起重绘,而重绘不一定会引起回流。

如果你的 HTML 变得很大很复杂,那么影响你 JavaScript 性能的可能并不是 JavaScript 代码的复杂度,而是页面的回流和重绘。


回流产生原因

  1. 初始化(Initial)。DOM 载入后的第一次回流,将会遍历所有 frame。

  2. 渐进(Incremental)。当一个 frame 发生渐进回流时,意味着它前面的元素都没有变, 而是它里面的元素变了。这会引起自底向上的作用。

  3. 改变大小(Resize)。元素的容器边界发生变化时,此时元素内部状态没变。 在计算自顶向下的布局约束的同时,可以复用内部状态。

  4. 样式改变(StyleChange)。整个 frame 树都应得到遍历。

  5. Dirty。当一个容器已经缓存了多个子元素的 Incremental 回流时,该容器出于 Dirty 的状态。


导致回流发生的因素

  1. 调整窗口大小,浏览器窗口尺寸改变——resize 事件发生时

  2. 元素尺寸改变——边距、填充、边框、宽度和高度;改变字体大小

  3. 页面渲染初始化

  4. 元素位置改变

  5. 样式表变动

  6. 元素内容变化,尤其是输入控件

  7. CSS 伪类激活

  8. DOM 操作,添加或者删除可见的 DOM 元素

  9. offsetWidth, width, clientWidth, scrollTop/scrollHeight 的计算, 会使浏览器将渐进回流队列 Flush,立即执行回流。内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变


例题(多选):修改以下哪些 CSS 属性会导致回流(reflow)?

A、color

B、width

C、display

D、font-size


答案:BCD

解析:因为改变颜色属性不会导致页面的排版改变


如何减少回流与重绘

减少回流、重绘其实就是需要减少对 render tree 的操作(合并多次多 DOM 和样式的修改),并减少对一些 style 信息的请求,尽量利用好浏览器的优化策略。

1. 直接改变 className,如果动态改变样式,则使用 cssText document.getElementById("div1").style.cssText = "color:red; font-size:13px;";

2. 让要操作的元素进行”离线处理”,处理完后一起更新

  • a) 使用 DocumentFragment 进行缓存操作,引发一次回流和重绘;

  • b) 使用 display:none 技术,只引发两次回流和重绘;

  • c) 使用 cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘;

3.不要经常访问会引起浏览器 flush 队列的属性,如果你确实要访问,利用缓存

4. 让元素脱离动画流,减少回流的 Render Tree 的规模

5.避免逐项更改样式。最好一次性更改 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性

6.避免循环操作 DOM。创建一个 documentFragment 或 div,在它上面应用所有 DOM 操作,最后再把它添加到 window.document。

7.避免循环读取 offsetLeft 等属性。在循环之前把它们存起来。

8.绝对定位具有复杂动画的元素。绝对定位使它脱离文档刘,否则会引起父元素及后续元素大量的回流。

例子:

var s = document.body.style; 
s.padding = "2px"; // 回流+重绘s.border = "1px solid red"; // 再一次 回流+重绘
s.color = "blue"; // 再一次重绘s.backgroundColor = "#ccc"; // 再一次 重绘
s.fontSize = "14px"; // 再一次 回流+重绘
// 添加node,再一次 回流+重绘document.body.appendChild(document.createTextNode('abc!'));
复制代码


一起学习,一起进步 -.- ,如有错误,可以发评论

发布于: 2021 年 05 月 13 日阅读数: 23
用户头像

空城机

关注

曾经沧海难为水,只是当时已惘然 2021.03.22 加入

业余作者,在线水文 主要干前端的活,业余会学学python 欢迎各位关注,互相学习,互相进步

评论

发布
暂无评论
HTML回流与重绘