为何操做DOM会影响WEB应用的性能?

面试官常常会问你:“平时工做中,你怎么优化本身应用的性能?”
你回答以下:“我平时遵循如下几条原则来优化个人项目、以提升性能,主要有:”javascript

a. 减小DOM操做的次数(减小DOM的获取与修改次数)css

b. 减小网络请求html

c. 压缩、合并静态资源文件(css、js、img等)前端

d. 小图片文件base64化处理vue

e. js少用全局变量java

f. ...web

Bingo!此时,你给本身刨了个能够把本身埋住的大坑。
由于面试官可能会追问你:“为何减小DOM操做能够提升性能?”面试

为何呢?
_______chrome

一、dom是什么?ES和 DOM是什么关系?

DOM就是Document Object Model,文档对象模型,里边是接口,即方法函数。咱们经过调用并传指定参数来使用。
官方定义:DOM是一个独立于语言的、用于操做XML和HTML文档的程序接口(API)。在浏览器中主要用于与HTML文档打交道,而且使用DOM API用来访问文档中的数据。
DOM是个与ES语言无关的API,它在浏览器中的接口倒是用JavaScript来实现的,DOM就成了如今JS编码中的重要部分。浏览器

1-一、各大浏览器中,DOM的位置和JavaScript的位置(渲染引擎与JS引擎相互独立)

浏览器 JS位置 DOM位置
IE JavaScript的实现名为JScript,位于jscript.dll文件中 DOM的实现则存在另外一个库中,名为mshtml.dll(内部称为trident)
safari JavaScript部分是由独立的SquirelFish引擎来实现。 DOM和渲染是使用webkit中的webcore实现
google chrome JavaScript引擎是他们本身研发的,名为V8。 使用webkit中的webCore库来渲染页面
firefox JavaScript引擎名为TraceMonkey 渲染引擎Gecko

1-二、ES和 DOM是两种东西

ES经过DOM接口来获取文档中的元素。
正由于浏览器中一般把DOM和ECMAScript独立实现。使得两者相互独立,就像两座孤岛。
因此ES每次操做DOM时,ES和DOM之间就像两个桥之间须要过车辆。
每次连接就都须要搭建一个桥梁,搭桥仍是小事,ES请求DOM的车辆过桥时,会通过一个收费站,每次都会被收费。JS引擎会消耗浏览器的性能进行缴费。
而车辆经过后桥就销毁,下次连接从新搭桥二次缴费。因此说JS与DOM每次链接都须要消耗性能
也正所以,有了每操做一次DOM就多作点事的理念,尽量以最少的次数处理最多的DOM操做,以实现每过一次桥多拉点货的效果。
(VUE也正是这种理念,操做虚拟dom减小性能消耗,所以vue性能更优,另个话题来讲。)

二、ES每次访问DOM都须要消耗性能:

正由于两者相互独立,因此每次连接、每次访问DOM都会消耗性能!! 能够说操做dom是十分昂贵的!!宁肯处理一万次js,也不操做一次dom!!

三、ES每次修改DOM元素的代价则更为昂贵

像上边说的,每次操做DOM以前,就会先访问DOM,因此也会消耗性能。
在此基础上,由于修改DOM会致使浏览器从新计算页面的几何变化、引起浏览器模板引擎的重排(回流 - 回滚流程)和重绘,进而更加消耗性能。

四、浏览器渲染引擎的工做原理、工做流程是什么?

浏览器下载完页面中的全部资源(好比HTML、JavaScript、CSS、图片等)后,会发生以下的6步过程:

  1. 解析HTML,构建DOM树(DOM Tree)
  2. 解析CSS,生成CSS规则树(CSSOM Tree)
  3. 合并DOM树和CSS规则树,生成渲染树render树(render Tree)
  4. 布局render树,根据生成的render树来对各元素尺寸、位置进行计算,获得每一个节点的几何信息。(根据视口的大小来计算元素的位置和大小)(重排会走这一步)
  5. 绘制render树,绘制页面像素信息(根据render树上每一个节点的几何信息,获得每一个节点的像素数)(重绘会走这一步)
  6. 浏览器会将各层节点的像素信息发送给GPU,GPU将各层合成、绘制展现到页面上

4-一、浏览器渲染引擎是如何生成渲染树(render Tree)的?

先看一张图:
浏览器如何生成渲染树

由上图得知以下流程:

  1. 从DOM Tree的根节点开始遍历每个可见节点(除meta、link、script等这些标签;除display:none;的元素)
  2. 对于每一个可见节点,在CSSOM中找到对应规则并将样式规则应用到对应节点上。
  3. 根据每个可见节点,以及其对应的样式,组合生成渲染树。

不可见节点: 不会渲染输出的节点(不会显示在屏幕上的节点)有如下几种

  • meta、link、script等标签;
  • 经过css进行隐藏的节点,即display:none;(opacity对人类不可见,计算机还能看见,因此还会渲染。)(那visibility为隐藏的元素会不会被渲染呢?作个试验,一个div设置visibility不可见,左浮动,周围全是文字,看文字环绕是否让出一块空白区域。最后试验证实确实绕出了一段空白的位置,说明visibility和opacity设置的不可见只是对人类肉眼不可见,计算机仍是会在生成render Tree的时候计算位置信息并把他绘制出来。试验结果以下图:)

visibility为隐藏的元素会不会被渲染

五、什么是浏览器渲染引擎的重排和重绘?

5-一、重排

当DOM的变化影响了元素的几何属性(宽和高),浏览器须要从新计算元素的几何属性,一样其余相邻元素的几何属性和位置也会所以受到影响。浏览器会使渲染树中受到影响的部分失效,并从新构造渲染树。这个过程称为“重排”。

换句话说,改变了页面中某元素的位置、尺寸大小,进而也就改变了他的占地面积。那这个元素修改了占地面积后,其后紧邻的元素就得挪动位置。给她让地儿(或者向前赶赶)。紧邻的元素挪动了,那紧邻元素后边的元素也会连锁效应式的修改。这就比如一排人排队。前边的人忽然变胖了、变瘦了、向前挪了、向后挤了、都会致使队伍中后边的人也跟随之改变位置,由此致使一连串的人都挪动位置。这时浏览器就要从新排版各个受到影响的元素的位置。反应在渲染引擎的工做流程中也就是浏览器须要从新计算元素位置信息并布局render树。这就是重排

5-二、重绘

完成重排后,浏览器会从新绘制受影响的部分到屏幕中,该过程称为重绘
由于重排在重绘的上一步,因此重排发生后天然会致使重绘。这个很好理解。

六、何时会引起重排?

当页面布局和几何属性改变时就须要重排:
(核心就是:只要某个属性能致使位置信息发生改变,就会触发重排 )

  1. 添加或删除可见的DOM元素。(一堆人排队,添加即中间插入了一我的/删除即中间一我的走了,势必会影响后边排队的人的位置信息也发生改变)
  2. 元素位置改变(重排就是由于位置信息改变了)
  3. 元素尺寸改变( 外边距、内边距、边框厚度、宽度、高度等)
  4. 内容改变,例:文本数量/内容改变、或图片被另外一个不一样尺寸的图片替代、字体大小改变、(文字加粗?)致使DOM元素位置、面积改变。【计算会消耗CPU的能力】
  5. 页面渲染器初始化(这算重走流程吧,确定要重排)
  6. 浏览器窗口尺寸改变(位置信息会被迫调整,发生重排。见下图的gif图,一个页面中div元素的位置不受视口调整而修改,也会引起重排)【消耗GPU的计算能力】

试验:resize视口,一个页面中div元素的位置不受视口调整而修改,也会引起重排
resize视口会引起重排

七、打断浏览器的优化步骤

现代浏览器是至关完善的了,由于屡次操做DOM会触发重排重绘、消耗性能。因此除了咱们人为的、有意识的去控制操做DOM次数之外,浏览器在设计上进行了优化,也会智能的“节流”操做DOM,好比实现队列化修改、批量执行。

解释来讲就是,浏览器会有一个“队列”,用以存放(攒着)须要操做DOM的js程序。每当执行一次js操做dom的代码,这个队列里就先暂存一个程序。等到一段时间后,浏览器再集中、批量的连接一次"ES岛"和"DOM岛"(就是让JS引擎去连接渲染引擎),进而触发一次DOM操做。你能够形象的理解为“过一段时间发一班车”。

可是咱们人类感知不到啊,可能会由于误操做打断浏览器的“节流”步骤。迫使浏览器中断当前的“等待”,去赶忙、立马进行一次dom操做。让浏览器赶忙执行完他攒在“队列”里的JS操做DOM的程序后返回最新的DOM位置信息给咱们。这就好像电梯门定时自动关闭,可是你却手动按了关门按钮强迫关门同样。

这种状况就发生在咱们获取DOM信息的时候:

打断浏览器优化,强迫触发重排的属性:
offsetTop、offsetLeft、offsetWidth、offsetHeight
scrollTop、scrollLeft、scrollWidth、scrollHeight
clientTop、clientLeft、clientWidth、clientHeight
getComputedStyle()

由于要跟浏览器请求最新的DOM信息,因此浏览器就得赶忙让JS引擎去渲染引擎那里进行一次DOM操做。

八、何时会引起重绘?

  1. 重排必然引起重绘,这是确定的。由于浏览器的工做流程就是排版后渲染。重排会回流(回滚流程)到排版阶段,排版后须要从新绘制页面。
  2. 单独触发重绘的状况:
    除元素尺寸、位置发生改变之外的状况,(好比字体颜色、背景色等发生改变)。(我怀疑文字加粗也会触发重排,可是我没有证据。理论上来讲若是在一个固定尺寸的div内加粗文字,应该不会影响后边元素的重排,但可能该div内部的其余相邻文字或元素会发生重排。)

试验gif图:
文字加粗会引起重排

(想到一个验证只发生重绘的状况,那就是后边也加点元素,若是重排了,后边的元素在控制台的检测下也会闪绿光。)

九、为何不提倡重排和重绘?

既然知道了这个dom操做会触发重排、重绘。那又是为何要尽可能避免重排和重绘呢?
换句话说,重排和重绘的反作用是什么?缺点是什么?

这就要引入CPU和GPU了。

重排会占用CPU,dom元素位置计算会消耗CPU的算力,因此应该尽可能减小CPU的占用,使电脑不卡顿。
重绘会占用GPU,渲染页面时会消耗GPU的算力。

GPU的分类:

  1. 家用GPU
    适合作贴图、特效、光影等效果。不适合画图形。
  2. 专业GPU
    适合画图形。不适合作贴图、特效、光影等效果。

DOM操做基本就是画图形的,但浏览器中用的就是家用GPU,其画图形耗费的性能是专业GPU的几十倍。因此不提倡频繁用装有家用GPU的浏览器绘制页面。也就是不提倡频繁触发重绘。

十、总结: 为何操做DOM很是昂贵?

  1. ES和 DOM是两种东西,每次链接都须要消耗性能
  2. 操做DOM会致使重排和重绘,重排会占用、消耗CPU; 重绘会占用、消耗GPU

十一、控制台观察一个页面的重排和重绘现象

由于重排必然会引起重绘,因此在浏览器的开发者工具中提供了一个检测重绘的按钮。寻找和打开步骤以下图:
控制台开启重绘检测按钮

各css属性对重排重绘的影响:https://csstriggers.com/

能够关注个人微信公众号看更多总结文章~
前端说吧

相关文章
相关标签/搜索