页面的重排(回流)和重绘以及优化

11 篇文章 0 订阅

一、HTML页面呈现流程

1.浏览器解析HTML,构建DOM树。HTML中的每个tag都是DOM树中的1个节点,根节点就是我们常用的document对象。DOM树表示页面的结构,里面包含了所有HTML标签,包括display:none隐藏,还有JS动态添加的元素等。
2.浏览器解析样式(用户定义的CSS和用户代理)生成Style Tree(样式结构体),在解析过程中会去掉浏览器不能识别的样式,比如IE会去掉-moz开头的样式,而FF会去掉_开头的样式。
3.合并DOM Tree和Style Tree生成Render Tree(渲染树),Render Tree类似于DOM Tree,但Render Tree能识别样式,它当中的每个NODE节点都有自己的style,且它不包含隐藏的节点(比如display:none的节点,head节点),因为这些节点不用于呈现,也不会影响呈现,所以不包含在Render Tree中。而visibility:hidden隐藏的元素还是会包含到Render Tree中,因为visibility会影响layout(布局),占有空间。Render Tree中的每个节点都被成为Box,理解页面元素为一个具有填充、边距、边框和位置的盒子。
在这里插入图片描述
4.布局render 树(Layout/Reflow),计算各元素尺寸、位置。
5.绘制render 树(Paint),绘制页面像素信息。

简单来说就是浏览器下载完页面所有资源后,就要开始构建DOM树和样式结构体(Style Tree),DOM树和Style Tree合并为渲染树(Render Tree)
在这里插入图片描述

如图为渲染的流程:js修改dom结构或样式---->计算style---->layout(重排)----->paint(重绘)------>composite(合成)

二、回流和重绘(Reflow/Layout &Repaint)

(一)概念

  • 回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的规模尺寸、布局或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。完成重排后,浏览器重新绘制受影响的部分到屏幕上,该过程为重绘。
  • 重绘:当渲染树中一些元素需要更新属性,这些属性只影响外观、风格,不影响布局,比如background-color。
  • 注意:简单的说,重排负责元素的几何属性更新,重绘负责元素的样式更新。重排一定会引起重绘,但重绘不一定引起重排。每个页面至少需要一次重排,即为页面第一次加载的时候。

(二)重排何时发生:
页面布局几何属性改变时就重排。下述情况会发生浏览器重排:

  • 添加或删除可见的DOM元素;
  • 元素位置改变;
  • 元素本身尺寸改变——边距、填充、边框、宽度和高度;
  • 内容改变——文本改变或图片大小改变引起的计算值宽度和高度改变;
  • 页面渲染初始化;
  • 浏览器窗口尺寸改变——resize事件发生时;

三、性能优化(减少重排、重绘)

(一)浏览器本身的优化策略
浏览器会维护一个队列,把所有引起重排、重绘的操作放入这个队列,等队列中的操作到了一定数量或时间间隔,浏览器就会flush队列,进行一个批处理。这样让多次的重排重绘变成一次。但有时候一些特殊的style属性会使这种优化失效。例如offsetTop, scrollTop, clientTop, getComputedStyle()IE中currentStyle)等属性,这些属性都是需要实时回馈给用户的几何属性或布局属性,因此浏览器不得不立即执行,并随之触发重排返回正确的值。
(二)最小化重绘和重排
减少对渲染树的操作,并减少对一些style信息的请求,尽量利用好浏览器的优化策略。
方法:

  1. 将多次改变样式属性的操作合并为一次操作(适用于单个存在的节点)
    例如:
 //javascript
 var el = document.querySelector('.el');
el.style.borderLeft = '1px';
el.style.borderRight = '2px';
el.style.padding = '5px';

可以使用内联样式的cssText方法实现:

var el = document.querySelector('.el');
el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px';

也可以使用切换类名的方法:

 //css
 .active {
    padding: 5px;
    border-left: 1px;
    border-right: 2px;
}
// javascript
var el = document.querySelector('.el');
el.className = 'active';

2.批量修改DOM
核心思想:

  • 让该元素脱离文本流
  • 对其进行多重改变
  • 将元素带回文档中

例如:

// html
<ul id="mylist">
  <li><a href="https://www.mi.com">xiaomi</a></li>
  <li><a href="https://www.miui.com">miui</a></li>
</ul>

// javascript 现在需要添加带有如下信息的li节点
let data = [
  {
    name: 'tom',
    url: 'https://www.baidu.com',
  },
  {
      name: 'ann',
      url: 'https://www.techFE.com'
  }
]

首先,我们先写一个通用的用于将新数据更新到指定节点的方法:

// javascript
function appendNode($node, data) {
  var a, li;
 
  for(let i = 0, max = data.length; i < max; i++) {
    a = document.createElement('a');
    li = document.createElement('li');
    a.href = data[i].url;
   
    a.appendChild(document.createTextNode(data[i].name));
    li.appendChild(a);
    $node.appendChild(li);
  }
}

首先我们忽视所有的重排因素,大家肯定会这么写:

let ul = document.querySelector('#mylist');
appendNode(ul, data);

使用这种方法,在没有任何优化的情况下,每次插入新的节点都会造成一次重排

(1)采用display属性隐藏元素,进行修改后,再显示该元素,这样只在隐藏和显示时触发2次重排;

let ul = document.querySelector('#mylist');
ul.style.display = 'none';
appendNode(ul, data);
ul.style.display = 'block';

将需要多次重排的元素,position属性设为absolute或fixed,这样元素就脱离了文档流,它的变化不会影响到其他元素,例如使用动画效果的元素最好设为绝对定位。
(2)使用文档片段创建一个子树,然后再拷贝到文档中。例如要异步获取表格数据,渲染到页面。可以先取得数据后在内存中构建整个表格的html片段,再一次性添加到文档中去,而不是循环添加每一行。这样只在添加文档片段的时候触发1次重排。

let fragment = document.createDocumentFragment();
appendNode(fragment, data);
ul.appendChild(fragment);

(3)将原始元素拷贝到一个独立的节点中,操作这个节点,覆盖原始元素

let old = document.querySelector('#mylist');
let clone = old.cloneNode(true);
appendNode(clone, data);
old.parentNode.replaceChild(clone, old);

3.缓存布局信息
前面提到,访问offsetTop这种属性时,会冲破浏览器本身的优化,所以我们应该减少对布局信息的查询次数。
在需要经常取那些引起浏览器重排的属性值时,将其赋值给局部变量,再使用局部变量参与计算。
例如:

div.style.left = 1 + div.offsetLeft + ‘px’;
div.style.top = 1 + div.offsetTop + ‘px’;

改进,避免重复取值:

current = div.offsetLeft;
div.style.left = 1 + ++current + ‘px’;
div.style.top = 1 + ++current + ‘px’;

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
重绘重排是CSS渲染过程中的两个重要概念。 重绘(Repaint)指的是当元素的样式发生变化,但不影响其布局时,浏览器会将新样式应用到元素上,重新绘制元素的外观。重绘的开销相对较小,不会引起布局的变化。 而重排(Reflow)指的是当页面布局发生变化时,例如修改了元素的尺寸、位置、内容等,浏览器会重新计算并更新元素的几何属性(如大小、位置),然后重新布局页面重排的开销相对较大,因为它涉及到整个页面或部分页面重新渲染。 重绘重排的区别在于是否引起布局的变化。重绘只会重新绘制元素的外观,而不会影响其周围元素的布局;而重排会导致整个渲染树的重新构建和布局。 在性能优化方面,我们通常要尽量减少重排重绘的次数,因为它们会消耗大量的计算资源。一些常见的优化方法包括: 1. 使用 CSS3 动画或过渡代替 JavaScript 实现的动画效果,因为后者可能会导致频繁的重排重绘; 2. 使用类似 flexbox 和 grid 等布局技术,可以减少页面布局的复杂性,降低重排重绘的次数; 3. 避免频繁访问会引起重排重绘的属性,例如 offsetTop、offsetLeft、scrollTop、clientWidth 等; 4. 批量更新样式或布局,可以使用 CSS 类名的方式一次性修改多个元素的样式,而不是逐个修改; 5. 将需要执行多次重排的 DOM 操作尽量合并为一次,使用文档片段(DocumentFragment)进行缓存。 通过合理优化和减少重排重绘的次数,可以提升页面的性能和响应速度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值