浏览器主要部件
浏览器主要部件图:
User Interface
用户接口。浏览器中的地址栏、前进后退、书签菜单等。除了网页显示区域以外的都是。
Brower engine
浏览器引擎。查询与操作渲染引擎的接口。
Rendering engine
渲染引擎。负责显示请求的内容
Data Persistence
持久层。HTML5规定了完整的浏览器中的数据库:web database
Networking
网络。用于网络请求,例如HTTP请求。
JavaScript Interpreter
用于解析执行JavaScript代码
UI Backend
绘制基础原件,比如组合框、窗口。
渲染引擎
渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。
渲染主流程图:
解析html以构建dom树 -> 构建render树 -> 布局render树 -> 绘制render树
Webkit内核渲染图:
Gecko内核渲染图:
尽管webkit和Gecko使用的术语稍有不同,他们的主要流程基本相同。Gecko称可见的格式化元素组成的树为frame树,每个元素都是一个frame,webkit则使用render树这个名词来命名由渲染对象组成的树。Webkit中元素的定位称为布局,而Gecko中称为回流。Webkit称利用dom节点及样式信息去构建render树的过程为attachment,Gecko在html和dom树之间附加了一层,这层称为内容接收器,相当制造dom元素的工厂。
分析上图可知浏览器的渲染页面分为以下几个步骤:
(1) 解析
- HTML/SVG/XHTML,事实上Webkit有三个C++的类对应这三类文档。解析这三种文件会产生一个DOM Tree。
- CSS,解析CSS会产生CSS Rule Tree。
- 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解析流程图:
解析由两部分组成:分词+构建树。
分词是词法分析,把输入解析成符号序列。构建树的过程就是在不断处理分词器完成的节点。除了把元素添加到DOM树上,还会将其添加到一个开放元素堆栈,用于纠正嵌套错误和标签未关闭错误。
但其实浏览器比我们想象的更强大更包容。浏览器的错误处理相当统一,虽然这不是当前HTML规范的一部分。当很多格式不良的HTML文档出现在很多网站,浏览器会尝试用和其他浏览器一样的方式修复错误。
DOM树构建图:
输出的树,也就是解析树,是由DOM元素及属性节点组成的。DOM和标签基本是一一对应的关系。
CSS树构建图:
渲染
渲染基本流程:
- 计算CSS样式
- 构建Render Tree
- Layout – 定位坐标和大小,是否换行,各种
position
,overflow
,z-index
属性 …… - 正式开画
注意:上图流程中有很多连接线,这表示了Javascript动态修改了DOM属性或是CSS属会导致重新Layout,有些改变不会,就是那些指到天上的箭头,比如,修改后的CSS rule没有被匹配到,等。这里重要要说两个概念,一个是reflow
,另一个是repaint
。
渲染树与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,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫reflow
)reflow
会从<html>
这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置,在reflow
过程中,可能会增加一些frame
,比如一个文本字符串必需被包装起来。
这些改变通常由以下事件触发:
- DOM操作(元素添加,删除,修改,或者元素顺序的改变);
- 内容变化,包括表单域内的文本改变;
- CSS属性的计算或改变(改变元素尺寸、位置);
- 添加或删除样式表(改变元素尺寸、位置);
- 更改
'class'
的属性(改变元素尺寸、位置); - 动画效果进行计算和改变 CSS 属性值(改变元素尺寸、位置);
- 浏览器窗口的操作(缩放,滚动);
- 当你修改网页的默认字体时。
- 伪类激活(
:hover
)。
reflow
的成本比repaint
的成本高得多的多。DOM Tree里的每个结点都会有reflow
方法,一个结点的reflow
很有可能导致子结点,甚至父点以及同级结点的reflow
。在一些高性能的电脑上也许还没什么,但是如果reflow
发生在手机上,那么这个过程是非常痛苦和耗电的。
当然,浏览器不会像上面那样,每改一次样式就 reflow
或 repaint
一次。一般来说,浏览器会把这样的操作积攒一批,然后做一次 reflow
,这又叫异步 reflow
或增量异步 reflow
。但是有些情况浏览器是不会这么做的,比如:resize
窗口,改变了页面默认的字体等。对于这些操作,浏览器会马上进行 reflow
。
浏览器渲染优化
- 创建有效的 HTML 和 CSS ,不要忘记指定文档编码,比如
<meta charset="utf-8">
。 - CSS 样式应该包含在
<head>
中, Javascript 脚本出现在<body>
末尾。 - 减少 CSS 嵌套层级和选择适当的选择器,可参考 如何提升 CSS 选择器性能。
- 不要通过 Javascript 逐条修改 DOM 的样式,提前定义好 CSS 的
class
进行操作。 - 尽量减少将 DOM 节点属性值放在循环当中,会导致大量读写此属性值。
- 尽可能的为产生动画的 HTML 元素使用
fixed
或absolute
的position
,那么修改他们的 CSS 是不会reflow
的。
注:display:none
会触发reflow
,而visibility:hidden
只会触发repaint
,因为没有发现位置变化。