从我打仗前端到现在,一向听到的一句话:操纵DOM的本钱很高,不要随意马虎去操纵DOM。尤其是React、vue等MV*框架的涌现,数据驱动视图的形式更加深入人心,jQuery时期供应的壮大方便地操纵DOM的API在前端工程里用的越来越少。寻根究底,这里说的本钱,究竟高在哪儿呢?
什么是DOM
Document Object Model 文档对象模子
什么是DOM?能够许多人第一回响反映就是div、p、span等html标签(最少我是),但要晓得,DOM是Model,是Object Model,对象模子,是为HTML(and XML)供应的API。HTML(Hyper Text Markup Language)是一种标记言语,HTML在DOM的模子规范中被视为对象,DOM只供应编程接口,却没法现实操纵HTML内里的内容。但在浏览器端,前端们能够用剧本言语(JavaScript)经由历程DOM去操纵HTML内容。
那末题目来了,只需JavaScript才挪用DOM这个API吗?
答案是NO。
Python也能够接见DOM。所以DOM不是供应给Javascript的API,也不是Javascript里的API。
PS: 实质上还存在CSSOM:CSS Object Model,浏览器将CSS代码剖析成树形的数据构造,与DOM是两个自力的数据构造。
浏览器衬着历程
议论DOM操纵本钱,肯定要先相识该本钱的泉源,那末就离不开浏览器衬着。
这里暂只议论浏览器拿到HTML以后最先剖析、衬着。(怎样拿到HTML资本的能够后续另开篇总结吧,什么握握握手啊挥挥招招手啊,万恶的flag…)
- 剖析HTML,构建DOM树(这里碰到外链,此时会提议要求)
- 剖析CSS,天生CSS划定规矩树
- 兼并DOM树和CSS划定规矩,天生render树
- 规划render树(Layout/reflow),担任各元素尺寸、位置的盘算
- 绘制render树(paint),绘制页面像素信息
- 浏览器会将各层的信息发送给GPU,GPU将各层合成(composite),显现在屏幕上
1.构建DOM树
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet">
<title>Critical Path</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg"></div>
</body>
</html>
不管是DOM照样CSSOM,都是要经由
Bytes → characters → tokens → nodes → object model
这个历程。
DOM树构建历程:当前节点的一切子节点都构建好后才会去构建当前节点的下一个兄弟节点。
2.构建CSSOM树
上述也提到了CSSOM的构建历程,也是树的构造,在终究盘算各个节点的款式时,浏览器都邑先从该节点的广泛属性(比方body里设置的全局款式)最先,再去运用该节点的详细属性。另有要注重的是,每一个浏览器都有本身默许的款式表,因而许多时刻这棵CSSOM树只是对这张默许款式表的部份替代。
3.天生render树
DOM树和CSSOM树兼并天生render树
简朴形貌这个历程:
DOM树从根节点最先遍历可见节点,这里之所以强调了“可见”,是因为假如碰到设置了相似display: none;
的不可见节点,在render历程当中是会被跳过的(但visibility: hidden; opacity: 0
这类依旧占有空间的节点不会被跳过render),保留各个节点的款式信息及其他节点的从属关联。
4.Layout 规划
有了各个节点的款式信息和属性,但不晓得各个节点的确实位置和大小,所以要经由历程规划将款式信息和属性转换为现实可视窗口的相对大小和位置。
5.Paint 绘制
万事俱备,末了只需将确定好位置大小的各节点,经由历程GPU衬着到屏幕的现实像素。
Tips
- 在上述衬着历程当中,前3点能够要屡次实行,比方js剧本去操纵dom、变动css款式时,浏览器又要从新构建DOM、CSSOM树,从新render,从新layout、paint;
- Layout在Paint之前,因而每次Layout从新规划(reflow 回流)后都要从新动身Paint衬着,这时候又要去斲丧GPU;
- Paint不一定会触发Layout,比方改个色彩改个背景;(repaint 重绘)
- 图片下载完也会从新动身Layout和Paint;
什么时候触发reflow和repaint
reflow(回流): 依据Render Tree规划(多少属性),意味着元素的内容、构造、位置或尺寸发作了变化,须要从新盘算款式和衬着树;
repaint(重绘): 意味着元素发作的转变只影响了节点的一些款式(背景色,边框色彩,笔墨色彩等),只须要运用新款式绘制这个元素就能够了;
reflow回流的本钱开支要高于repaint重绘,一个节点的回流往往回致使子节点以及同级节点的回流;
GoogleChromeLabs 内里有一个csstriggers,列出了各个CSS属性对浏览器实行Layout、Paint、Composite的影响。
引发reflow回流
当代浏览器会对回流做优化,它会比及充足数目的变化发作,再做一次批处理回流。
- 页面第一次衬着(初始化)
- DOM树变化(如:增删节点)
- Render树变化(如:padding转变)
- 浏览器窗口resize
- 猎取元素的某些属性:
浏览器为了取得准确的值也会提早触发回流,如许就使得浏览器的优化失效了,这些属性包含offsetLeft、offsetTop、offsetWidth、offsetHeight、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height、挪用了getComputedStyle()或许IE的currentStyle
引发repaint重绘
- reflow回流一定引发repaint重绘,重绘能够零丁触发
- 背景色、色彩、字体转变(注重:字体大小发作变化时,会触发回流)
优化reflow、repaint触发次数
- 防止逐一修正节点款式,只管一次性修正
- 运用DocumentFragment将须要屡次修正的DOM元素缓存,末了一次性append到实在DOM中衬着
- 能够将须要屡次修正的DOM元素设置
display: none
,操纵完再显现。(因为隐蔽元素不在render树内,因而修正隐蔽元素不会触发回流重绘) - 防止屡次读取某些属性(见上)
- 将庞杂的节点元素离开文档流,下降回流本钱
为何一再强调将css放在头部,将js文件放在尾部
DOMContentLoaded 和 load
- DOMContentLoaded 事宜触发时,仅当DOM加载完成,不包含款式表,图片…
- load 事宜触发时,页面上一切的DOM,款式表,剧本,图片都已加载完成
CSS 资本壅塞衬着
构建Render树须要DOM和CSSOM,所以HTML和CSS都邑壅塞衬着。所以须要让CSS尽早加载(如:放在头部),以收缩初次衬着的时候。
JS 资本
壅塞浏览器的剖析,也就是说发明一个外链剧本时,需守候剧本下载完成并实行后才会继承剖析HTML
- 这和之前文章提到的浏览器线程有关,浏览器中js引擎线程和衬着线程是互斥的,详见《从setTimeout-setInterval看JS线程》
一般的剧本会壅塞浏览器剖析,加上defer或async属性,剧本就变成异步,可比及剖析终了再实行
- async异步实行,异步下载终了后就会实行,不确保实行递次,一定在onload前,但不确定在DOMContentLoaded事宜的前后
- defer耽误实行,相对于放在body末了(理论上在DOMContentLoaded事宜前)
举个栗子
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet">
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg"></div>
<script src="app.js"></script>
</body>
</html>
- 浏览器拿到HTML后,从上到下递次剖析文档
- 此时碰到css、js外链,则同时提议要求
- 最先构建DOM树
- 这里要特别注重,因为有CSS资本,CSSOM还未构建前,会壅塞js(假如有的话)
- 不管JavaScript是内联照样外链,只需浏览器碰到
script
标记,叫醒JavaScript剖析器
,就会举行停息blocked
浏览器剖析HTML,并比及CSSOM
构建终了,才实行js剧本 - 衬着首屏(DOMContentLoaded 触发,实在不一定是首屏,能够在js剧本实行前DOM树和CSSOM已构建完render树,已paint)
首屏优化Tips
说了这么多,实在能够总结几点浏览器首屏衬着优化的方向
- 削减资本要求数目(内联亦或是耽误动态加载)
- 使CSS款式表尽早加载,削减@import的运用,因为须要剖析完款式表中一切import的资本才会算CSS资本下载完
- 异步js:壅塞剖析器的 JavaScript 会强迫浏览器守候 CSSOM 并停息 DOM 的构建,致使初次衬着的时候耽误
- so on…
晓得操纵DOM本钱多高了吗?
实在写了这么多,觉得偏题了,大批的材料参考的是chrome开发者文档。觉得js剧本资本那块照样有点乱,包含和DOMContentLoaded的关联,愿望大家能多多指导,多多指摘,感谢大佬们。
操纵DOM详细的本钱,说究竟是形成浏览器回流reflow和重绘reflow,从而斲丧GPU资本。
参考文献:
https://developers.google.com/web/fundamentals/performance/critical-rendering-path/
已同步至个人博客-
软硬皆施Github 迎接star :)