转发至https://www.jianshu.com/p/05eb1b17b298
一 : 为什么要了解浏览器渲染页面的机制,主要还是性能的优化。
- 了解浏览器如何进行加载,我们可以在引用外部样式文件,外部JS时,将它们放到合适的位置,是浏览器以最快的速度,将文件加载完毕。
- 了解浏览器如何进行解析,我们可以在构建DOM结构,组织CSS选择器的时候,选择最优的写法,提高浏览器的解析速率。
- 了解浏览器如何进行渲染,明白渲染的过程,我们在设置元素属性,编写JS文件时,可以减少“重绘”,“重新布局”的消耗。
二 : 要了解清楚渲染机制,要先弄明白DOM、CSSOM**
-
DOM:Document Object Model,浏览器将HTML解析成树形的数据结构,简称DOM。
-
CSSOM:CSS Object Model,浏览器将CSS解析成树形的数据结构,简称CSSOM。
-
Render Tree:DOM和CSSOM合并后生成Render Tree,如下图:
-
Layout:计算出Render Tree每个节点的具体内容
-
Painting:通过显卡,将Layout后的节点内容分别呈现到屏幕上。
三、注意事项
-
当浏览器获得HTML文件后,会自上而下的加载,并在加载过程中进行解析和渲染。加载说的就是获取资源文件的过程,如果在加载过程中遇到外部CSS文件和图片,浏览器会另外发送一个请求,去获取CSS文件和相应的图片,这个请求是异步的,并不会影响HTML文件的加载。
-
但是如果遇到Javascript文件,HTML文件会挂起渲染的进程,等待JavaScript文件加载完毕后,再继续进行渲染。
-
为什么HTML需要等待JavaScript呢? 因为JavaScript可能会修改DOM,导致后续HTML资源白白加载,所以HTML必须等待JavaScript文件加载完毕后,再继续渲染,这也就是为什么JavaScript文件在写在底部body标签前的原因。
四 : 浏览器渲染的整个流程
-
当用户输入一个URL时,浏览器就会向服务器发出一个请求,请求URL对应的资源
-
接受到服务器的响应内容后,浏览器的HTML解析器,会将HTML文件解析成一棵DOM树,DOM树的构建是一个深度遍历的过程,当前节点的所有子节点都构建完成以后,才会去构建当前节点的下一个兄弟节点。
-
将CSS解析成CSSOM树(CSS Rule Tree)
-
根据DOM树和CSSOM树,来构建Render Tree(渲染树),注意渲染树并不等于DOM树,因为一些像
head
或display:none
的东西,就没有必要放在渲染树中了。 -
有了Render Tree,浏览器已经能知道网页中有哪些节点,各个节点的CSS定义,以及它们的从属关系,下一步操作就是
Layout
,顾名思义,就是计算出每个节点在屏幕中的位置。 -
Layout后,浏览器已经知道哪些节点要显示,每个节点的CSS属性是什么,每个节点在屏幕中的位置是哪里,就进入了最后一步painting,按照算出来的规则,通过显卡,把内容画到屏幕上。
-
这里还要说两个概念,一个是Reflow,另一个是Repaint。这两个不是一回事。
- Repaint ——改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸没有变。
- Reflow ——元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。
- reflow 几乎是无法避免的。现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显示与隐藏)等,都将引起浏览器的 reflow。鼠标滑过、点击……只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲染。通常我们都无法预估浏览器到底会 reflow 哪一部分的代码,它们都彼此相互影响着。
注: display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。
五 : DOM和CSSOM的具体构建流程
DOM 和 CSSOM 都是以" Bytes → characters → tokens → nodes → object model. " 这样的方式生成最终的数据。如下图所示:
-
当服务器返回一个HTML文件给浏览器的时候,浏览器接受到的是一些字节数据。
-
然后浏览器根据HTTP响应中的编码方式(通过是UTF-8),解析字节数据,得到一些字符。如果这个时候编码方式跟文件的字节编码不一致,便会出现乱码。所以我们通过使用<meta http-equiv="content-type"content=“text/html;charset=utf-8”>来告诉浏览器我们页面使用的是什么编码。
-
这个时候,浏览器再根据DTD中的对元素(标签)的定义,对这些接受到的字符进行语义化(token)。我们经常在html文件的第一行,定义,这个DTD就是告诉浏览器,哪些字符是有意义的,哪些字符是无意义的。 DTD常见的有严格、过渡、框架和HTML5三种。不同的DTD中,有不同的元素定义。比如过渡的DTD中就比严格的DTD中多出了
、 等等一些元素的定义,也就是说使用严格的DTD,即使你在你的HTML文件中使用了 ,浏览器也会忽略掉的。所以也就有了一些人使用不同的DTD或者不使用DTD的时候,布局会明显不同的原因了。 -
接着,浏览器再使用这些语义块(token)创建对象,形成一个个节点了。
-
然后HTML解析器就会从HTML文件的头部到尾部,一个个地遍历这些节点。当这些节点是普通节点的话,HTML解析器就会将这些节点加入到DOM树中。当这些节点是JS代码的话,HTML解析器就会将控制权交给JS解析器。如果这些节点是CSS代码的话,HTML解析器就会将控制权交给CSS解析器。不过,当外联的JS代码和CSS代码还没从服务器传到浏览器的时候,这个时候如果DOM树上有可视元素的话,浏览器通常会选择在这个时候,将一些内容提前渲染到屏幕上来。
-
当HTML解析器读到最后一个节点的时候,整个DOM树也构建完成了,这个时候就会触发domContentloaded事件。而很多JS库(像JQ)通常会在这个时候有所反应的。
至此,DOM树就全部构建完成了。