渲染引擎简介
本文所讨论的浏览器——Firefox、Chrome和Safari是基于两种渲染引擎构建的,Firefox使用Geoko——Mozilla自主研发的渲染引擎,Safari和Chrome都使用webkit。
渲染引擎首先通过网络获得所请求文档的内容,通常以8K分块的方式完成。下面是渲染引擎在取得内容之后的基本流程:
1. 解析HTML文件,创建DOM树
浏览器把得到的html代码转换为一个DOM树,html文档中的每一个tag标签都是一个DOM树的节点(文本节点也是),DOM树的根节点就是我们的document对象。这里要注意,我们用js动态生成的DOM节点也在DOM树上。
DOM 树的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。
上图展示了从html的字节码被浏览器处理为DOM的过程
转换:根据字节的编码规则将其转化为特定字符,也就是characters
生成tokens:将character转化为w3c定义的各种特定标签 ,生成tokens(令牌)
词法解析:匹配字符串,将tokens按照规则转换为包含特定属性和规则的节点对象(nodes)
DOM构建:根据每个节点的层次关系和规则转换为直观的树形结构,具有明确的父子关系。
值得一提的是,HTML都是增量构建的,在HTML文件还在传输时html parse就可以开始了,最终我们到了页面完整的文档对象模型(DOM),在以后的页面渲染包括布局、绘制等都会用到它。
2. 解析CSS,生成CSS Rule Tree
浏览器会把所有的样式解析为样式结构体(包括css样式和浏览器默认样式),当然浏览器识别不了的样式不能解析,最后生成了CSSOM树。
上图是CSSOM构建流程图,跟DOM构建差不多,将CSS文件的字节码转换为符合浏览器特定规则的字符,然后浏览器对其进行解析和构成树。
与DOM有所不同的是,其整个的计算过程略有复杂,包括一套复杂的特异度计算规则(CSS属性来源 -> 特异度大小 -> 书写顺序前后覆盖),最终确定每个节点的样式值形成下图的不完整CSSOM。
CSS一直被认为是一种渲染阻塞资源(所谓CSS白屏),因为渲染树是依赖CSSOM才能生成,进而走浏览器的布局渲染流程,所以我们才有了CSS放在head的最佳实践。
优先级:浏览器默认设置<用户设置<外部样式<内联样式<HTML中的style样式。
3. 将CSS与DOM合并,构建渲染树(renderingtree)
它是由DOM树和CSSOM树合成的,渲染树的每一个节点都有自己的style样式,渲染树并不等同于 DOM 树,渲染树上没有隐藏的节点,比如display:block和无样式head节点,因为这些节点不会呈现也不影响呈现(visibility:hidden会存在渲染树,因为它占有空间,会影响布局)
渲染树生成大概经过以下过程:
从DOM根节点开始遍历每个在HTML和CSS意义上的可见节点。
对于每个可见节点,为其找到适配的CSSOM并且组合他们
将每个节点(包括内容和样式)组建成render-tree
可见节点:渲染树包含了渲染网页所需的所有节点,不需要渲染的节点是不会合并到渲染树中的,比如元数据元素meta,base等,还有设置了display:none的节点。
4. 布局(layout)计算渲染树节点大小
有了渲染树,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。下一步操作就是计算出每个节点在屏幕中的位置。在这个阶段会根据渲染树的样式计算布局,输出的结果称为box盒模型。
盒模型精确表示了每一个元素的位置和大小,所有相对度量单位都转化为了绝对单位。将相对转换为绝对,这就需要首先明确或定义好相对的一个标准,是相对谁的相对值,如rem是相对根元素的font-size值,vw是相对视口的width等。
简单介绍涉及到的viewport和html的font-size值
device-width为浏览器的理想视口
在移动端,如果不设置viewport宽度为理想视口,viewport宽度通常为980px,这会导致文字很小,我们需要手动放大阅读。
rem是 font size of the root element,简单一点可以设置html的字体大小为固定值(一般默认为16px),则width直接使用5rem(换算为80px),也可以使用js根据viewport大小动态设置rem大小。
5. 绘制(paint)
根据background, border, box-shadow等样式和HTML内容,将Layout生成的区域填充为最终将显示在屏幕上的像素。最后将信息渲染到屏幕中每一个真实的像素点,这个过程叫绘制或者rasterizing格栅化。
注意
上述这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html都解析完成之后再去构建和布局render树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
Javascript的加载和执行的特点:
(1)js的下载和执行会阻塞Dom树的构建,载入后马上执行;
(2)执行时会阻塞页面后续的内容(包括页面的渲染、其它资源的下载)。原因:因为浏览器需要一个稳定的DOM树结构,而JS中很有可能有 代码直接改变了DOM树结构,比如使用 document.write 或 appendChild,甚至是直接使用的location.href进行跳转,浏览器为了防止出现JS修 改DOM树,需要重新构建DOM树的情况,所以 就会阻塞其他的下载和呈现。
HTML页面加载和解析流程
1. 用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件;
2. 浏览器开始载入html代码,发现<head>标签内有一个<link>标签引用外部CSS文件;
3. 浏览器又发出CSS文件的请求,服务器返回这个CSS文件;
4. 浏览器继续载入html中<body>部分的代码,并且CSS文件已经拿到手了,可以开始渲染页面了;
5. 浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;
6. 服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;
7. 浏览器发现了一个包含一行 Javascript 代码的<script>标签,赶快运行它;
8. Javascript 脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个<div> (style.display=”none”)。突然少了这么一个元素,浏览器不得不重新渲染这部分代码;
9. 终于等到了</html>的到来,浏览器泪流满面……
10. 等等,还没完,用户点了一下界面中的“换肤”按钮,Javascript 让浏览器换了一下<link>标签的CSS路径;
11. 浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。
网友评论