最近,在研究为啥React的虚拟dom那么快的时候,我意识到咱们不太了解javascript的性能。因此我写这篇文章以帮助提升对Repaint,Reflow,JavaScript性能的认识。javascript
一图胜千言。因此,咱们从较高层面来看下浏览器的工做原理!css
hmm...“browser engine(浏览器引擎)”和“rendering enging(渲染引擎)”是啥?html
浏览器引擎的主要工做是把HTML文档和网页的其它资源转换成用户可交互可视化的界面。 除了“browser engine”,还有另外经常使用的两个术语:“layout engine(布局引擎)” 和 “rendering enging(渲染引擎)”。理论上,布局和渲染处理的引擎能够单独分开。不过实际上,它俩彼此紧密耦合因此不太会分开来讲。java
当你点击或输入某个url,浏览器会向该页面发起一个http请求,而且相应的服务器返回HTML文档。(固然中间还有不少细节会发生)node
浏览器解析出HTML源代码并构建一棵DOM树,这棵DOM树就是HTML节点的数据层表现,在这棵树里每一个HTML标签都有一个对应的节点,标签之间的文本也会获得一个text node(文本节点)。这课DOM树的根节点就是documentElement
(<html>
标签)react
浏览器解析CSS代码。样式信息级联:基本(样式)规则在用户代理样式表中(浏览器默认值),接着多是用户样式表,网页做者的样式表 - 外部的,导入的,内联的(就是把样式写在HTML标签的属性里)。算法
接下来是有趣的部分 - 构建渲染树。渲染树有点像DOM树,可是不彻底是。渲染树知道样式,例如你用display:none
来隐藏一个div
的时候,它将不会在渲染树中表示。其它看不见的元素也同样,好比head
及在它里面的全部内容。另外一方面,渲染树中可能存在用多个节点来表示(一个)DOM元素 - 好比文本节点,例如<p>
节点里每一行都须要一个渲染节点。渲染树里的一个节点被叫作box(盒子模型)。每一个(渲染)节点都一个css盒子属性 - width, height, border, margin,,等。api
渲染树创建好之后,浏览器就能把渲染树节点画到屏幕上。浏览器
这里有个短视频,浏览器是若是在屏幕上画节点的。 视频-画的动态效果缓存
看不了的同窗,我这里放一张静态图,感觉一下:
(视频里的过程)咱们可能根本就感受不到,发生在几分之一秒内。
浏览器是如何布局,如何检测根节点,兄弟节点以及孩子节点,以及相应地从新排列他们的布局。
<html>
<head>
<title>Repaint And Reflow</title>
</head>
<body>
<p>
<strong>How's The Josh?</strong>
<strong><b> High Sir...</b></strong>
</p>
<div style="display: none">
Nothing to display
</div>
<div><img src="..." /></div>
...
</body>
</html>
复制代码
DOM树里基本每一个标签都有一个节点对应,节点之间的每一个文本都有一个文本节点(这里简单起见,让咱们忽略一下,其实一个空白(好比换行)也是一个文本节点)。
documentElement (html)
head
title
body
p
strong
[text node]
p
strong
b
[text node]
div
[text node]
div
img
...
复制代码
而渲染树是DOM树的可视部分。不过他会少一点东西-head和hidden的div,不过也有额外复数行的文本节点。
root (RenderView)
body
p
line 1
line 2
line 3
...
div
img
...
复制代码
渲染树的根节点包含了全部元素的。你能够认为(这个根节点)就是浏览器里面的那部分。WebKit把这个根节点称做RenderView
并把它与css里的初始包含块对应起来,也就是页面的顶部(0,0)到(window.innerWidth, window.innerHeight)的可视区域。
要弄清楚哪些东西须要显示在屏幕上,这个涉及到渲染树的递归遍历。
一般(页面)都须要至少一次layout(回流)和一次重绘(除非,你的页面是空的:))。以后,改变渲染树都会致使如下一种或两种事情发生:
重绘 和 回流 消耗比较大,他们会下降用户体验以及卡UI。
顾名思义,重绘只不过是屏幕上从新绘制元素,由于元素更改的外观会影响元素的可见性,但不会影响布局。 好比下面几个将会触发重绘:
回流是从新计算文档中元素的位置和几何形状,以便从新显示文档的部分或所有。由于回流会阻止用户操做,因此开发者有必要知道怎样优化回流时间,了解各类文档属性(DOM深度,CSS效率,不一样类型的样式更改)对回流时间的影响。有时候回流单个元素可能会致使它的父元素们以及它其后的全部元素的回流。
每次DOM有改变,浏览器须要从新计算CSS,回流及重绘页面。这就是为啥真实DOM比较耗时。
为了最小化这个耗时,Ember 使用了 key/value 观察者技术,Angular使用了脏检测。用这种技术,就可以只更新变化了的dom节点(在Angular里被标记为脏的节点)。
不过,现代浏览器也变得聪明了,尝试缩短重绘屏幕所需的时间。最大的变化就是可以重绘批处理DOM的更改。
React虚拟dom背后的理念就是减小和缓存dom变化
React没有作什么新的事情,只是在策略上有动做。它把真实DOM存一份副本到内存里。当你修改dom了,他先把(这个修改)应用到内存里的DOM,而后使用它的对比算法,找出哪一个是真的有变化。
最后,它(react)批量处理这些变化而且一次性应用到真实的DOM上。因此,最小化了回流和重绘。