160826、浏览器渲染页面过程描述,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,这样此元素就脱离了文档流,不会影响其他元素。

 

转载于:https://www.cnblogs.com/zrbfree/p/5818627.html

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

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

相关文章

小程序 pagescrollto_微信小程序学习笔记(三)-- 首页及详情页开发

一、常用组件在上一个章节中讲解了封装请求数据的模块&#xff0c;在此处请求轮播图的数据1.首页轮播图数据的请求以及渲染1.1 轮播图数据的请求 pages/home/home.jsimport 2 使用组件 - 视图容器 - swiper滑块视图容器。其中只可放置swiper-item组件&#xff0c;否则会导致未定…

云计算与边缘计算协同 九大应用场景

来源&#xff1a;物联网报告中心前言2019 年边缘计算备受产业关注&#xff0c;一度引起了资本市场的投资热潮&#xff0c;很多人把2019 年称作边缘计算的元年。理性来看&#xff0c;造成如此火爆局势难免有一些炒作因素在推波助澜&#xff0c;毕竟边缘计算的概念存世也已多年。…

cat3 utp是不是网线_网线/双绞线上各标识CAT, AWG, PR, UTP/STP/FTP/SFTP的含义

CAT5, CAT5e, CAT6表示网线类别, 常见的有 CAT5, CAT5e, CAT6分别表示五类, 超五类, 六类网线24AWG, 26AWGAmerican Wire Gauge是美制电线标准, 表示电缆直径. AWG值是导线厚度(以英寸计)的函数, 24AWG约为0.511mm&#xff0c;4pr表示有四对双绞线UTPUnshielded Twisted Pair 非…

Wireshark过滤器表达式的规则

1、抓包过滤器语法和实例 抓包过滤器类型Type&#xff08;host、net、port&#xff09;、方向Dir&#xff08;src、dst&#xff09;、协议Proto&#xff08;ether、ip、tcp、udp、http、icmp、ftp等&#xff09;、逻辑运算符&#xff08;&& 与、|| 或、&#xff01;非&…

《深入浅出WPF》学习笔记之深入浅出话属性

依赖属性是一种可以从父级元素继承&#xff0c;并且可以通过Binding从数据源获取&#xff0c;当从父级继承时不占用内存的属性系统。拥有依赖属性的对象称为依赖对象。WPF允许在创建对象时并不分配用于存储数据的空间&#xff0c;而在需要时实时分配空间或直接借用父级数据&…

sql 创建表时AUTO_INCREMENT=10

默认地&#xff0c;主键的开始值是 1&#xff0c;每条新记录递增 1&#xff0c;AUTO_INCREMENT10 代表初始值为10。

一年代码功能点的创新性怎么写_查新报告的查新点(创新点)写法,参考

创新点(查新点)写法参考(要求&#xff1a;尽量具体&#xff0c;简明扼要&#xff0c;突出核心亮点&#xff0c;用专业术语。不要写的笼统、抽象、宽泛、上位)。可从结构、技术、参数、功能等方面考虑(并非全部要写)&#xff1a;1、结构方面的创新(前面部分是具体的创新结构形式…

查询手机号段对应地区编码_2020陕西专升本考试今日起查询成绩啦

专升本考试顺利结束,我们终于迎来了验收成果的时刻,每位参与专升本的同学都是最棒的,现在 我们一起去揭秘考试成绩吧&#xff01;专升本成绩查询指南成绩查询及志愿填报登陆“陕西省教育考试院”或“陕西省招生考试信息网”陕西招生考试信息网http://www.sneac.com/陕西省教育…

李德仁院士:5G时代空间信息如何实现智能服务

李德仁院士来源&#xff1a;光明日报【编者按】随着5G时代的到来&#xff0c;物联网、云计算、大数据等技术快速发展。信息基础设施日益完善&#xff0c;全球时空大数据呈爆发式增长。在这样的时代背景下&#xff0c;如何实现空间感知和空间认知的智能化&#xff0c;在正确的时…

个人对响应式布局的理解

从字面上理解&#xff0c;响应式布局即CSS布局随着屏幕尺寸的变换而变化。首先我们要搞清楚什么是固定布局&#xff0c;什么是流式布局。固定布局(Fixed Layout)使用固定宽度的包裹(Wrapper), 内部的各个部分可以使用百分比或者固定的宽度来表示. 这里最重要的是, 外面的所谓包…

jmeter 不同场景 比例_在JMeter测试中如何根据业务场景来控制运行比例

性能测试混合场景中&#xff0c;我们需要组合多个业务操作到场景中来。比如有一个论坛的业务分布如下&#xff1a;开新帖与回复帖子的比例为2:3&#xff0c;那么我们在JMeter测试计划中如何控制其比例呢&#xff1f;下面我们介绍两种方式&#xff1a;1.多线程组方式2.逻辑控制器…

python爬取新浪新闻首页_学习了《python网络爬虫实战》第一个爬虫,爬取新浪新闻...

请安装anaconda,其中附带的spyder方便运行完查看变量 1.进入cmd控制台&#xff0c; 输入 pip install BeautifulSoup4 pip install requests 2.编写代码&#xff0c;代码已经很清晰了&#xff0c;直接运行不会报错并有成功的结果def getNewsDetail(newsUrl): import requests f…

sql去重查询

例如&#xff0c;从 "Websites" 表的 "country" 列中选取唯一不同的值 SELECT DISTINCT country FROM Websites;

计算机和人类“看”法不同,但这重要么?

来源&#xff1a;大数据文摘“看”这个动作一直被认为是生物所独有的。当工程师第一次“教”计算机“看”这个动作时&#xff0c;他们理所当然地认为计算机可以像人类眼睛一样进行观看。约克大学&#xff08;York University&#xff09;计算机科学家约翰托索斯&#xff08;Joh…

获取项目根路径,并在其下创建一个名称为userData 的目录。

NSArray *pathsNSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES); NSString *documentsDirectory [paths objectAtIndex:0]; // 创建文件系统管理器 NSFileManager *fileManager [[NSFileManageralloc] init]; // 判断userData 目录是否存在 …

svn拉取文件合并_SVN的分支使用

使用过git版本管理的同学大部分都使用过分支&#xff0c;git创建切换分支非常直观&#xff0c;SVN则相对来说没有那么强的分支的概念。本文主要介绍SVN中的分支使用。SVN默认的生成目录如下通常 branches 目录下为开发分支&#xff0c;tags 目录为标签目录&#xff0c;trunk 为…

python编程入门单例_python实现单例模式怎么写啊?

这个网上有很多的&#xff0c;你可以搜一搜&#xff0c;单例设计模式很多面试官都会考的&#xff0c;最好是记住一两个&#xff0c;我下面写一个例子给你看一下 使用__new__方法 在__new__方法中把类实例绑定到类变量_instance上&#xff0c;如果cls._instance为None表示该类还…

超级智能的定义,一个已经诞生并不断深刻影响人类的新智能

来源&#xff1a;今日头条在人工智能领域&#xff0c;有一个不成熟的观点&#xff0c;认为当人工智能发展成为“在几乎所有领域都大大超过人类认知表现的任何智力”时&#xff0c;这时人工智能可以被视作超级人工智能或超级智能。但这种从人工智能延伸的超级智能因为在理论和实…

MySQL修改数据表存储引擎的3种方法介绍

看你的mysql现在已提供什么存储引擎:mysql> show engines;看你的mysql当前默认的存储引擎:mysql> show variables like %storage_engine%;你要看某个表用了什么引擎(在显示结果里参数engine后面的就表示该表当前用的存储引擎):mysql> show create table 表名; MySQL作…