读书笔记 --- 再次阅读回流与重绘

参考 - 强烈推荐看看,这个作者写了很多特别好的文章.

浏览器渲染过程

  1. 解析HTML,生成DOM树; 解析CSS生成CSSOM树
  2. 将DOM树和CSSOM树合并,生成渲染(Render)树
  3. Layout(回流): 根据生成的渲染树,视口(viewport),得到节点的几何信息(位置、大小)
  4. Painting(重绘): 根据渲染树和几何信息得到节点的绝对像素
  5. Display: 将像素发送给GPU,展示在页面上

生成渲染树

image-20200221150055606

为了构建渲染树,浏览器主要完成了以下工作:

  1. 从DOM树的根节点开始遍历每个可见节点
  2. 对于每个可见的节点,找到CSSOM树中的规则,并应用它们
  3. 根据每个可见节点及其对应的样式,组合生成渲染树

【不可见的节点】:

  • 一些不会渲染输出的节点: 比如script、meta、link等
  • 一些通过css进行隐藏的节点。比如display: none。注意,利用visibility和opacity隐藏的节点,还是会显示在渲染树上的。只有display:none的节点才不会显示在渲染树上

【注意】: 渲染树只包括可见的节点

回流(Layout)

前面将DOM节点以及它对应的样式结合起来,可是我们还需要计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流。看下面的栗子:

<!DOCTYPE html>
<html><head><meta name="viewport" content="width=device-width,initial-scale=1"><title>Cretical Path: Hello Marron!</title></head><body><div style="width: 50%"><div style="width: 50%">Hi Marron, best wish!</div></div></body>
</html>

我们可以看到,第一个div将节点的显示尺寸设置为视口宽度的50%,第二个div将其尺寸设置为父节点的50%.而在回流这个阶段,我们就需要根据视口具体的宽度,将其转为实际的像素值。

preview

重绘 (Painting)

  • 生成渲染树阶段: 我们直到了哪些节点是可见的以及可见节点的样式
  • 在回流阶段: 我们得到了可见元素的具体几何信息

我们得到的信息,最终都会托付给GPU进行渲染

GPU的渲染需要具体的像素位置,这就是重绘阶段所做的事情: 根据渲染树和几何信息计算出绝对像素点.

何时发生回流重绘

回流主要是计算节点的几何位置和几何像素大小.那么当页面布局和几何信息发生变化的时候,就需要回流:

  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(内/外边距、边框大小、高度和宽度等)
  • 内容发生: 文本发生变化或图片被另一个不同尺寸的图片所替代
  • 页面刚开始渲染的时候
  • 浏览器的窗口尺寸变化: 回流是根据视口的大小来计算元素的位置和大小的

经典老话: 回流一定重绘,重绘不一定回流

浏览器的优化机制

现代的浏览器都是很聪明的,由于每次重排都会造成造成额外的计算消耗,因此大多数浏览器都会通过队列修改、批量执行来优化重排过程。浏览器会将修改操作放在队列里,直到过了一段时间,或者操作达到一个阈值,才清空队列。

还有一些强制刷新的属性(避免使用):

  • offsetTop、offsetLeft、offsetWidth、offsetHeight
  • scrollTop、scrollLeft、scrollWidth、scrollHeight
  • clientTop、clientLeft、clientWidth、clientHeight
  • getComputedStyle()
  • getBoundingClientRect

前端优化

1 -【并多次的DOM和添加样式】

// 未优化前 - 3次
const el = document.getElementById('test')
el.style.padding = '5px';
el.style.borderLeft = '1px';
el.style.borderRight = '2px';// 合并样式 - 1次
const el = document.getElementById('test');
el.style.cssText += 'border-left: 1px; border-right: 2px; padding: 5px'// 添加样式 - 1次
const el = document.getElementById('test');
el.calssName += ' active';

2 -【脱离文档流】

当元素脱离文档流后,对元素的所有操作都不会引起回流和重绘.因此如果,对某个元素进行的DOM操作比较多的时候,可以先将元素脱离文档流,然后操作,最后在放回文档流。具体操作如下:

  1. 使元素脱离文档流
  2. 对其进行多次修改
  3. 将元素带回到文档中.

[注] : 上述的1、3会引起回流和重绘.

【脱离文档流的方法】

  • 隐藏元素,修改应用,重新显示
  • 使用文档片段(document fragment)在使用DOM之外构建一个子树,再把它拷贝回文档
  • 将原始元素拷贝到一个脱离文档的节点中,修改节点后,再替换原始的元素。
// 每次插入li都会引起一次回流和重绘
function appendDataToElement(appendToElement, data) {let li;for(let i =0,len = data.length;i < len;i++){li = document.createElement('li');li.textContent = 'text';appendToElement.appendChild(li);}
}const ul = document.getElementById('list');
appdenDataToElement(ul, data);

[隐藏元素]

// 仅在隐藏元素和现实元素时产生2次回流和重绘
function appendDataToElement(appendToElement, data) {let li;for(let i =0, len = data.length; i < len; i++){li = document.createElement('li');li.textContent = 'text';appendTOElement.appendChild(li);}
}const ul = document.getElementById('list');
ul.style.display = 'none';
appendDataToElement(ul, data);
ul.style.display = 'block';

[使用文档片段] - 在当前DOM外构建一个子树,再把它拷贝回文档

const ul = document.getElementById('list');
const fragment = document.createDocumentFragment();
appendDataToElement(fragment, data);
up.appendChild(fragment);

[脱离文档] - 将原始元素拷贝到一个脱离文档的节点中,修改节点,再替换原始的元素。

const ul = document.getElementById('list');
const clone = ul.cloneNode(true);
appendDataToElement(clone, data);
ul.parentNode.replaceChild(clone, ul);

[注] - 现代浏览器使用了队列来存储多次修改,因此上述的优化可能效果不是很理想.

3 - 【避免触发同步布局事件】

// 栗子: 多次使用到 offsetWidth 属性
function initP(){for(let i = 0; i< paragraph.length; i++){paragraph[i].style.width = box.offsetWidth + 'px'}
}

上述代码每次循环,都会使浏览器强制刷新队列(box.offsetWidth),造成多次回流和重绘.改进如下:

const width = box.offsetWidth;
function initP(){for(let i = 0; i < paragraph.length; i++){paragraph[i].style.width = width + 'px'}
}

4 - 【复杂动画的优化】

对于复杂动画效果,由于会经常的引起回流和重绘。因此,我们可以使用绝对定位,让它脱离文档流。否则会引起父元素以及后续元素频繁的回流 - 栗子

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/250280.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2017-2018 ACM-ICPC, Asia Daejeon Regional Contest

C 有n个节点和m边条&#xff0c;求一条最长的路径&#xff0c;该路径(c1,c2,c3...cn)满足 不出现重复的节点&#xff0c;ci 和ci1是邻居节点&#xff0c;且 ci 的邻居节点数量小于ci1的邻居节点数量。 记忆DFS遍历&#xff0c;每次递归计算的值都保存在数组里&#xff0c;这样复…

装系统工具

安装如果失败,注意是不是工具的版本太老导致 系统分区工具: DiskGeniusPortable 刻录工具: UlraISO rufus https://rufus.ie/ win32diskimager 转载于:https://www.cnblogs.com/jiangfeilong/p/9937164.html

小程序WXML基本使用

数据绑定 <!--wxml--> <view> {{message}} </view> // page.js Page({data: {message: Hello MINA!} }) 列表渲染 <!--wxml--> <view wx:for"{{array}}"> {{item}} </view> // page.js Page({data: {array: [1, 2, 3, 4, 5]} })…

tomcat 虚拟路径 与 虚拟主机配置

虚拟路径配置 方法一&#xff1a;此方法需要重启服务 打开下面文件 在host里面添加context标签 <Context docBase"D:\test" path"/testServlet/aaaaa" reloadable"true" /> 浏览器访问&#xff1a;http://172.16.6.103:1080/testServlet/a…

20172328 2018-2019《Java软件结构与数据结构》第八周学习总结

20172328 2018-2019《Java软件结构与数据结构》第八周学习总结 概述 Generalization 本周学习了二叉树的另一种有序扩展&#xff1f;是什么呢&#xff1f;你猜对了&#xff01;ヾ(◍∇◍)&#xff89;&#xff9e;就是堆。本章将讲解堆的链表实现and数组实现&#xff0c;以及往…

javascript --- Vue初始化 模板渲染

不带响应式的Vue缩减实现 模板 现有模板如下: <div id "app"><div class"c1"><div titlett1 id"id">{{ name }}</div><div titlett2 >{{age}}</div><div>hello3</div></div><ul>…

#RANK_1 极其简单的递归——骑士与金币

2000:金币 总时间限制: 1000ms内存限制: 65536kB描述国王将金币作为工资&#xff0c;发放给忠诚的骑士。第一天&#xff0c;骑士收到一枚金币&#xff1b;之后两天&#xff08;第二天和第三天&#xff09;里&#xff0c;每天收到两枚金币&#xff1b;之后三天&#xff08;第四、…

javascript --- vue2.x中原型的使用(拦截数组方法) 响应式原理(部分)

说明 在Vue2.x中,利用了对原型链的理解,巧妙的利用JavaScript中的原型链,实现了数组的pop、push、shift、unshift、reverse、sort、splice等的拦截. 你可能需要的知识 参考 - MDN 原型链 JavaScript常被描述为一种基于原型的语言(prototype-based language),每个对象拥有一…

dubbo-admin构建报错

dubbo-admin构建报错 意思是maven库里没有dubbo2.5.4-SNAPSHOT.jar这个版本的dubbo的jar包&#xff0c;把dubbo-admin项目的pom.xml的   <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>${proje…

PyCharm 通过Github和Git上管理代码

1.Pycharm中设置如图: 2.配置Git,通过网页 https://www.git-scm.com/download/win 下载 3. 转载于:https://www.cnblogs.com/0909/p/9956406.html

享元模式-Flyweight(Java实现)

享元模式-Flyweight 享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。 本文中的例子如下: 使用享元模式: 小明想看编程技术的书, 就到家里的书架上拿, 如果有就直接看, 没有就去买一本, 回家看. 看完了就放到家里…

013.Zabbix的Items(监控项)

一 Items简介 Items是从主机里面获取的所有数据&#xff0c;可以配置获取监控数据的方式、取值的数据类型、获取数值的间隔、历史数据保存时间、趋势数据保存时间、监控key的分组等。通常情况下item由key参数组成&#xff0c;如监控项中需要获取cpu信息&#xff0c;则需要一个对…

算法 --- 记一道面试dp算法题

题目: 给定一个数组(长度大于1),如下 let a [1,4,3,4,5] // 长度不确定,数值为整数要求写一个函数,返回该数组中,除本身数字之外其他元素的成积.即返回如下: // 过程[4*3*4*5, 1*3*4*5, 1*4*4*5, 1*4*3*5, 1*4*3*4] // 结果[240, 60, 80, 60, 48]题目要求不使用除法,且时间…

ASP.NET MVC 实现页落网资源分享网站+充值管理+后台管理(10)之素材管理

源码下载地址&#xff1a;http://www.yealuo.com/Sccnn/Detail?KeyValuec891ffae-7441-4afb-9a75-c5fe000e3d1c 素材管理模块也是我们这个项目的核心模块&#xff0c;里面的增删查改都跟文章管理模块相同或者相似&#xff0c;唯一不同点可能是对附件的上传处理&#xff0c;但…

Git很简单--图解攻略

Git Git 是目前世界上最先进的分布式版本控制系统&#xff08;没有之一&#xff09;作用 源代码管理为什么要进行源代码管理? 方便多人协同开发方便版本控制Git管理源代码特点 1.Git是分布式管理.服务器和客户端都有版本控制能力,都能进行代码的提交、合并、. 2.Git会在根…

vc/vs开发的应用程序添加dump崩溃日志转

原贴地址&#xff1a;https://blog.csdn.net/wangkui1331/article/details/78029940 vc/vs开发的应用程序出现崩溃的时候&#xff0c;由于没有任何记录&#xff0c;导致开发人员很难追踪&#xff0c;但是添加dump文件后&#xff0c;就可以免除这些烦恼 1.添加方法 &#xff08;…

51 nod 1127最短的包含字符串(尺取法)

1127 最短的包含字符串 收藏关注给出一个字符串&#xff0c;求该字符串的一个子串S&#xff0c;S包含A-Z中的全部字母&#xff0c;并且S是所有符合条件的子串中最短的&#xff0c;输出S的长度。如果给出的字符串中并不包括A-Z中的全部字母&#xff0c;则输出No Solution。Input…

JSON 数据重复 出现$ref

JSONArray 类型 如果我们往里面add数据的时候 如果数据相同&#xff0c;那么就会被替换成 $ref: 也就是被简化了 因为数据一样所直接 指向上一条数据 循环引用&#xff1a;当一个对象包含另一个对象时&#xff0c;fastjson就会把该对象解析成引用。引用是通过$ref标示的&am…

Linux初学时的一些常用命令(4)

1. 磁盘 查看当前磁盘使用情况 df -h查看某个文件大小 du -sh 文件名 如果不输入文件名&#xff0c;默认是当前目录的所有文件之和&#xff0c;即当前目录大小 2. 系统内存 free参数详解&#xff1a;https://blog.csdn.net/loongshawn/article/details/51758116 3. CPU CPU 使用…

爬虫之拉勾网职位获取

重点在于演示urllib.request.Request()请求中各项参数的 书写格式 譬如&#xff1a; url data headers...Demo演示&#xff08;POST请求&#xff09;:import urllib.requestimport urllib.parseimport json, jsonpath, csvurl "https://www.lagou.com/jobs/positionAjax.…