浏览器渲染原理总结

浏览器主要部件

浏览器主要部件图:
浏览器主要部件图
User Interface
用户接口。浏览器中的地址栏、前进后退、书签菜单等。除了网页显示区域以外的都是。
Brower engine
浏览器引擎。查询与操作渲染引擎的接口。
Rendering engine
渲染引擎。负责显示请求的内容
Data Persistence
持久层。HTML5规定了完整的浏览器中的数据库:web database
Networking
网络。用于网络请求,例如HTTP请求。
JavaScript Interpreter
用于解析执行JavaScript代码
UI Backend
绘制基础原件,比如组合框、窗口。

渲染引擎

渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。

渲染主流程图:
渲染主流程图.png
解析html以构建dom树 -> 构建render树 -> 布局render树 -> 绘制render树

Webkit内核渲染图:
Webkit内核渲染

Gecko内核渲染图:
Gecko内核渲染

尽管webkit和Gecko使用的术语稍有不同,他们的主要流程基本相同。Gecko称可见的格式化元素组成的树为frame树,每个元素都是一个frame,webkit则使用render树这个名词来命名由渲染对象组成的树。Webkit中元素的定位称为布局,而Gecko中称为回流。Webkit称利用dom节点及样式信息去构建render树的过程为attachment,Gecko在html和dom树之间附加了一层,这层称为内容接收器,相当制造dom元素的工厂。

分析上图可知浏览器的渲染页面分为以下几个步骤:
(1) 解析

  1. HTML/SVG/XHTML,事实上Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。
  2. CSS,解析CSS会产生CSS Rule Tree。
  3. Javascript,脚本,主要是通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree。

(2) 渲染
解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。
注意:Rendering Tree 渲染树并不等同于DOM树,因为一些像head元素或display:none的东西就没必要放在渲染树中了。CSS 的 Rule Tree主要是为了完成匹配并把CSS Rule附加上Rendering Tree上的每个Element,也就是DOM结点,即所谓的Frame。然后计算每个Frame(也就是每个Element)的位置,这又叫layout和reflow过程。

(3) 绘制
最后通过调用操作系统Native GUI的API绘制。

解析

HTML解析流程图:
HTML解析流程
解析由两部分组成:分词+构建树。
分词是词法分析,把输入解析成符号序列。构建树的过程就是在不断处理分词器完成的节点。除了把元素添加到DOM树上,还会将其添加到一个开放元素堆栈,用于纠正嵌套错误和标签未关闭错误。
但其实浏览器比我们想象的更强大更包容。浏览器的错误处理相当统一,虽然这不是当前HTML规范的一部分。当很多格式不良的HTML文档出现在很多网站,浏览器会尝试用和其他浏览器一样的方式修复错误。

DOM树构建图:
DOM树构建
输出的树,也就是解析树,是由DOM元素及属性节点组成的。DOM和标签基本是一一对应的关系。

CSS树构建图:
CSS解析

渲染

渲染基本流程:

  1. 计算CSS样式
  2. 构建Render Tree
  3. Layout – 定位坐标和大小,是否换行,各种position, overflow, z-index属性 ……
  4. 正式开画

渲染基本流程
注意:上图流程中有很多连接线,这表示了Javascript动态修改了DOM属性或是CSS属会导致重新Layout,有些改变不会,就是那些指到天上的箭头,比如,修改后的CSS rule没有被匹配到,等。这里重要要说两个概念,一个是reflow,另一个是repaint

渲染树与DOM树的关系

渲染树与对应DOM树图:
渲染树与对应DOM树
渲染对象和Dom元素相对应,但这种对应关系不是一对一的,不可见的Dom元素不会被插入渲染树,例如head元素。另外,display属性为none的元素也不会在渲染树中出现(visibility属性为hidden的元素将出现在渲染树中)。

还有一些Dom元素对应几个可见对象,它们一般是一些具有复杂结构的元素,无法用一个矩形来描述。例如,select元素有三个渲染对象——一个显示区域、一个下拉列表及一个按钮。同样,当文本因为宽度不够而折行时,新行将作为额外的渲染元素被添加。另一个多个渲染对象的例子是不规范的html,根据css规范,一个行内元素只能仅包含行内元素或仅包含块状元素,在存在混合内容时,将会创建匿名的块状渲染对象包裹住行内元素。

一些渲染对象和所对应的Dom节点不在树上相同的位置,例如,浮动和绝对定位的元素在文本流之外,在两棵树上的位置不同,渲染树上标识出真实的结构,并用一个占位结构标识出它们原来的位置。

重绘和重排

当用户与网页交互,或者脚本程序改动修改网页时,前文提到的一些操作将会重复执行,因为网页的内在结构已经发生了改变,即发生重排或重绘。

重绘(repaint)

屏幕的一部分要重画,比如某个CSS的背景色变了。但是元素的几何尺寸没有变。当改变那些不会影响元素在网页中的位置的元素样式时,譬如background-color(背景色), border-color(边框色), visibility(可见性),浏览器只会用新的样式将元素repaint一次(这就是repaint,或者说重新构造样式)。

重排(reflow/relayout)

当改变影响到文本内容或结构,或者元素位置时,reflow或者说重新布局就会发生。reflow意味着元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是reflow,或是Layout。(HTML使用的是flow based layout,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫reflowreflow 会从<html>这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置,在reflow过程中,可能会增加一些frame,比如一个文本字符串必需被包装起来。

这些改变通常由以下事件触发:

  • DOM操作(元素添加,删除,修改,或者元素顺序的改变);
  • 内容变化,包括表单域内的文本改变;
  • CSS属性的计算或改变(改变元素尺寸、位置);
  • 添加或删除样式表(改变元素尺寸、位置);
  • 更改'class'的属性(改变元素尺寸、位置);
  • 动画效果进行计算和改变 CSS 属性值(改变元素尺寸、位置);
  • 浏览器窗口的操作(缩放,滚动);
  • 当你修改网页的默认字体时。
  • 伪类激活(:hover)。

reflow的成本比repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法,一个结点的reflow很有可能导致子结点,甚至父点以及同级结点的reflow。在一些高性能的电脑上也许还没什么,但是如果reflow发生在手机上,那么这个过程是非常痛苦和耗电的。

当然,浏览器不会像上面那样,每改一次样式就 reflowrepaint 一次。一般来说,浏览器会把这样的操作积攒一批,然后做一次 reflow ,这又叫异步 reflow 或增量异步 reflow 。但是有些情况浏览器是不会这么做的,比如:resize窗口,改变了页面默认的字体等。对于这些操作,浏览器会马上进行 reflow

浏览器渲染优化

  1. 创建有效的 HTML 和 CSS ,不要忘记指定文档编码,比如<meta charset="utf-8">
  2. CSS 样式应该包含在<head>中, Javascript 脚本出现在<body>末尾。
  3. 减少 CSS 嵌套层级和选择适当的选择器,可参考 如何提升 CSS 选择器性能。
  4. 不要通过 Javascript 逐条修改 DOM 的样式,提前定义好 CSS 的class进行操作。
  5. 尽量减少将 DOM 节点属性值放在循环当中,会导致大量读写此属性值。
  6. 尽可能的为产生动画的 HTML 元素使用fixedabsoluteposition,那么修改他们的 CSS 是不会 reflow 的。
    注:display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。

参考文献