浏览器渲染页面过程描述,DOM编程技巧以及重排和重绘。
一、浏览器渲染页过程描述
1、浏览器解析html源码,然后创建一个DOM树。
在DOM树中,每一个HTML标签都有一个对应的节点(元素节点),并且每一个文本也都有一个对应的节点(文本节点)。
DOM树的根节点就是documentElement,对应的是html标签。
2、浏览器解析CSS代码,计算出最终的样式数据。
对CSS代码中非法的语法它会直接忽略掉。
解析CSS的时候会按照如下顺序来定义优先级:浏览器默认设置,用户设置,外联样式,内联样式,html中的style(嵌在标签中的行间样式)。
3、创建完DOM树并得到最终的样式数据之后,构建一个渲染树。
渲染树和DOM树有点像,但是有区别。DOM树完全和html标签一一对应,而渲染树会忽略不需要渲染的元素(head、display:none的元素)。
渲染树中每一个节点都存储着对应的CSS属性。
4、当渲染树创建完成之后,浏览器就可以根据渲染树直接把页面绘制到屏幕上。
二、高性能Javascript DOM编程
我们知道,用脚本进行DOM操作的代价很昂贵。把DOM和ECMAScript各自想象为一个岛屿,它们之间用收费桥梁链接,ECMAScript每次访问DOM,都要经过这座桥,并交纳“过桥费”,访问DOM的次数越多,费用也就越高。因此,推荐的做法是尽量减少过桥的次数,努力呆在ECMAScript岛上。这是比喻非常形象。那么怎样才能高效呢?
1、DOM访问与修改
访问DOM元素是有代价的(这里其实和ajax调后台数据接口是一样,DOM是用于操作XML和HTML文档的应用程序接口,一次能完成的事不要请求多次),通过DOM获取元素之后,修改元素的代价更是昂贵,因为它会导致浏览器重新计算页面的几何变化(重排和重绘)。
例子:
var times = 15000;
//code1
console.time('time1');
for(var i=0; i<times; i++){
document.getElementById('myDiv1').innerHTML +='a';
}
console.timeEnd('time1');
//code2
console.time('time2');
var str = '';
for(var i=0; i<times; i++){
str += 'a';
}
document.getElementById('maDiv2').innerHTML = str;
console.timeEnd('time2');
结果time1:2352.064ms/time2:0.789ms
第一段代码的问题在于,每次循环迭代,改元素都会被访问两次:一次读取innerHTML的值,另一次重写它。
结果充分证明,访问DOM的次数越多,代码的运行速度越慢。
因此,能减少DOM访问的次数就尽量减少,尽量留在ECMAScript这端处理。
2、html集合&遍历DOM
操作DOM另一个耗能点就是遍历DOM,在平时获取一组元素的时候(getElementsByTagName方法),收集的结果是一个类数组对象,它处于一种“实时状态”实时存在,这意味着当底层文档对象更新时,它也会自动更新。
例子:
而这正是低效之源!很简单,跟数组的优化操作一样,缓存一个length变量就OK了(读取一个集合的length比读取一个普通数组的length要慢很多,因为每次都要查询):
console.time('time0');
var lis0 = document.getElementsByTagName('li');
var str0 = '';
for(var i=0; i<lis0.length; i++){
str0 +=lis0[i].innerHTML;
}
console.timeEnd('time0');
console.time('time1');
var lis1 = document.getElementsByTagName('li');
var str1 = '';
for(var i=0; len=lis1.length; i<len; i++){
str1 += lis1[i].innerHTML;
}
console.timeEnd('time1');
结果:time0:0.237ms/time1:0.099ms
当获取的这个类数组对象长度值特别大的时候(如1000),性能提升还是很明显的。
第一部分主要提了两点优化,一是尽量减少DOM的访问,把运算放在ECMAScript这一端,二是尽量缓存局部变量,比如类数组对象的length。
三、重排和重绘
1、什么是重排和重绘
浏览器下载完页面中的所有组件(html标记、Javascript、CSS、图片)之后会解析生成两个内部数据结构——DOM树和渲染树。
DOM树表示页面结构,渲染树表示DOM节点如何显示。DOM树中的每一个需要显示的节点在渲染树中至少存在一个对应的节点(隐藏的DOM元素 display:none 在渲染树中没有对应的节点)。
渲染树中的节点被称为‘帧’或‘盒’,符合CSS模型的定义,页面元素为一个具有填充,边距,边框和位置的盒子。一旦DOM树和渲染树构建完成,浏览器就开始绘制页面元素。
当DOM的变化影响了元素的几何属性(宽或高),浏览器需要重新计算元素的几何属性,同时其他元素的几何属性和位置也会受到影响。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树,这个过程称为重排。完成重排后,浏览器会重新绘制受到影响的部分到屏幕,这个过程称为重绘。
由于浏览器的流布局,对渲染树的计算通常只需要遍历一次就可以完成。table及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花三倍同等元素的时间。这也是为什么我们要避免使用table做布局的一个原因。
并不是所有的DOM变化都会影响几何属性,比如改变一个元素的背景色并不会影响元素的宽和高,这种情况下只会发生重绘。
2、重排何时发生
a、添加或删除可见的DOM元素
b、元素位置改变
c、元素尺寸改变
d、元素内容改变
e、页面渲染初始化
f、浏览器窗口尺寸改变
3、渲染树变化的排队和刷新
获取布局信息的操作会导致队列刷新,比如:
a、offsetTop,offsetLeft,offsetWidth,offsetHeight
b、scrollTop,scrollLeft,scrollWidth,scrollHeight
c、clientTop,clientLeft,clientWidth,clientHeight
d、getComputeStyle()||currentStyle(IE)
因为offsetHeight属性需要返回最新的布局信息,因此浏览器不得不执行渲染队列中的“待处理变化”并触发重排以返回正确的值(即使队列中 改变的样式属性和想要获取的属性值并没有什么关系)。
4、最小化重排和重绘
改变元素多种样式的时候,最好用className,一次性完成操作,这样只会修改一次DOM。
5、文档碎片
文档碎片的设计初衷就是为完成这类任务——更新和移动节点。当你附加一个片段到节点时,实际上被添加的是该片段的子节点,而不是片段本身。只会触发一次重排,而且只访问一次实时的DOM。
用法:
var oFragment = document.createDocumentFragment();
for(var i=0; i<1000; i++){
var oP = document.createElement('p');
oP.innerHTML = i;
oFragment.appendChild(oP);
}
var oDo = document.body;
oDo.appendChild(oFragment);
6、让动画脱离文档流
使用绝对位置定位页面上的动画元素,将其脱离文档流。这样不会导致文档流中的元素受到影响,不会大规模的进行重排和重绘。
第二部分重排和重绘是DOM编程中耗能的主要原因之一,为了避免不必要的性能损耗可以参考一下几点:
1、尽量不要在布局信息改变时做查询(会导致渲染队列强制刷新);
2、同一个DOM的多个属性改变可以写在一起(减少DOM访问,同时把强制渲染队列刷新的风险降为0);
3、如果要批量添加DOM,可以先让元素脱离文档流,操作完成后带人文档流,这样只会触发一次重排(fragment元素的应用);
4、将需要多次重排的元素,添加定位属性,设置为absolute,fixed,这样此元素就脱离了文档流,不会影响其他元素。
浏览器渲染页面过程描述,DOM编程技巧以及重排和重绘。的更多相关文章
- 160826、浏览器渲染页面过程描述,DOM编程技巧以及重排和重绘
一.浏览器渲染页过程描述 1.浏览器解析html源码,然后创建一个DOM树. 在DOM树中,每一个HTML标签都有一个对应的节点(元素节点),并且每一个文本也都有一个对应的节点(文本节点). DO ...
- 浅谈浏览器解析 URL+DNS 域名解析+TCP 三次握手与四次挥手+浏览器渲染页面
(1)浏览器解析 URL 为了能让我们的知识层面看起来更有深度,我们应该考虑下面两个问题了: 从浏览器输入 URL 到渲染成功的过程中,究竟发生了什么? 浏览器渲染过程中,发生了什么,是不是也有重绘与 ...
- 浏览器渲染详细过程:重绘、重排和 composite 只是冰山一角
https://juejin.im/entry/590801780ce46300617c89b8 渲染 这张很经典的图许多人都看过,其中的概念大家应该都很熟悉,也就是这么几个步骤:js修改dom结构或 ...
- 网页性能优化:防止JavaScript、CSS阻塞浏览器渲染页面
网页中引用的外部文件: JavaScritp.CSS 等常常会阻塞浏览器渲染页面.假设在 <head> 中引用的某个 JavaScript 文件由于各种不给力需要2秒来加载,那么浏览器渲染 ...
- chrome和Firefox浏览器渲染页面的不同
一直很好奇chrome和firefox这两大浏览器的页面渲染有什么不同,今天自己写了些html代码来做了下检验. 先做html编码,代码如下: <!DOCTYPE html><htm ...
- 浏览器渲染页面的时候,不同的script块之间的关系
浏览器渲染页面时,当读到script元素的时候,浏览器中的js引擎会分多个script代码块来读取,不同的script代码出错互不影响,但是由于script中的变量作用域是全局,所以前面代码块声明的变 ...
- 浏览器渲染页面原理,reflow、repaint及其优化
浏览器的主要组件包括: 1. 用户界面 - 包括地址栏.前进/后退按钮.书签菜单等.除了浏览器主窗口显示的你请求的页面外,其他显示的各个部分都属于用户界面. 2. 浏览器引擎 - ...
- JS性能--DOM编程之重排与重绘
浏览器下载完页面中的所有组件----HTML标记,Js,CSS,图片等之后会解析并生成两个内部数据结构: DOM树 -------- 表示页面结构 渲染树 -------- 表示DOM节点如何显 ...
- DOM的回流和重绘(重排、重绘)
什么是DOM回流? 页面渲染时,我们对HTML结构简单的增删查改时,浏览器会对所有的dom进行重新排序,这就i是DOM回流,严重影响浏览器性能 DOM的回流和重绘: **DOM的回流**:当页面中元素 ...
随机推荐
- Hacker(13)----搜集目标计算机的重要信息
扫描与嗅探攻防 黑客若要发起入侵攻击,他需要做好充分的准备的工作,首先通过嗅探和扫描等操作来搜索信息,从而确定目标计算机,以便准确发出攻击.嗅探和扫描操作可以利用专业的软件工具实现,如X-Scan.S ...
- xtrabackup备份恢复测试
http://blog.chinaunix.net/uid-20682026-id-3319204.html
- python- 迭代器与生成器
1.迭代器: 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么, 因为人们很少在迭代途中往后退.另外,迭代器的一 ...
- ext.net中ComboBox空间实现模糊查询
ComboBox中的属性添加Mode="Local"可以实现第一个字的模糊查询但是搜索中间的字无法实现 现提供一下方法使用正则表达式实现全模糊查询 <ext:ComboBox ...
- FULL JOIN 与 CROSS JOIN
FULL JOIN 只要其中某个表存在匹配,FULL JOIN 关键字就会返回行.(返回JOIN 两端表的所有数据,无论其与另一张表有没有匹配.显示左连接.右连接和内连接的并集) FULL JOIN ...
- FOR UPDATE SKIP LOCKED
SYS_UNIT 中有UNIT_ID 0000000001 0000000002 0000001100 原意为若启用多线程,则每个线程在获取时仅可以获取一条数据(互斥) 脚本如下 1.SELECT * ...
- select选项改变时获取选中的option的值
本来天真的以为直接this.value()就能去到select的值,可事实并非那么回事. <script> document.getElementById('select').onchan ...
- EC读书笔记系列之1:条款1、条款2、条款3
条款1:视C++为一个语言联邦 记住: ★C++高效编程守则视状况而变化,这取决于你使用C++的哪一部分 C: Object-oriented c++: Template c++: STL 条款2:尽 ...
- JDBC学习入门
一.JDBC相关概念介绍 1.1.数据库驱动 这里的驱动的概念和平时听到的那种驱动的概念是一样的,比如平时购买的声卡,网卡直接插到计算机上面是不能用的,必须要安装相应的驱动程序之后才能够使用声卡和网卡 ...
- Oracle创建主键自增表
Oracle创建主键自增表 1.创建表 create table Test_Increase( userid number(10) NOT NULL primary k ...