网页解析
可以将浏览器整体看作一个网页处理模块,这个模块的输入是网络上接收到的字节流形式的网页内容。输出是三棵树型逻辑结构:DOM树、Render树及RenderLayer树
浏览器的解析过程就是将字节流形式的网页内容构建成DOM树、Render树及RenderLayer树的过程
浏览器的解析对象是网页内容,网页内容包括以下三个部分:
1、HTML文档:超文本标记语言,制作Web页面的标准语言
2、CSS样式表(Cascading Style Sheet):级联样式表,用来控制网页样式,并允许样式信息与网页内容相分离的一种标记性语言
3、JavaScript脚本:JavaScript是一种无类型的解释型脚本语言。常用于为网页添加动态功能
HTML文档决定了DOM树及Render树的结构。CSS样式表决定了Render树上节点的排版布局方式。JavaScript代码可以操作DOM树,改变DOM树的结构,也可以用来给页面添加更丰富的动态功能
HTML文档被解析生成DOM树,由DOM节点创建Render树节点时,会触发CSS匹配过程,CSS匹配的结果是RenderStyle实例,这个实例由Render节点持有,保存了Render节点的排版布局信息。CSS的解析过程即是CSS语法在浏览器的内部表示过程,解析的结果是得到一系列的CSS规则。CSS的匹配过程主要依据CSS选择器的不同优先级进行,高优先级选择器优先适用。根据网页上定义的JavaScript脚本的不同属性,JavaScript脚本的下载和执行时机会有所不同。JavaScript脚本的执行是由渲染引擎转交给JS引擎执行的。下面分别看一下HTML、CSS、JavaScript的具体解析和执行
1⃣️DOM树构建
DOM(Document Object Model,文档对象模型),是中立于平台和语言的接口。它允许程序和脚本动态地访问和更新文档的内容结构和样式。DOM是页面上数据和结构的一个树形表示,使用DOM接口可以对DOM树结构进行操作。DOM规范只是定义了编程接口,没有对文档的表示方式做任何限制。以树状结构表示DOM文档是比较普遍的实现方式。这个树状结构就称为DOM树。DOM树是DOM文档中的节点按照层次组织构成的。以HTML文档为例,每一个标签都对应着DOM树上的一个节点。由于是树形结构表示,这些节点之间的关系也是通过父子或兄弟维系的
渲染引擎解析HTML文档的过程就是将字节流形式的网页内容解析成DOM Tree、Render Tree、Render Layer Tree三棵树的过程。这个过程可以分为解码、分词、解析、建树四个步骤
1、解码:将网络上接收到的经过编码的字节流,解码成Unicode字符
2、分词:按照一定的切词规则,将Unicode字符流切成一个个的词语(Tokens)
3、解析:根据词语的语义,创建相应的节点(Node)
4、建树:将节点关联到一起,创建DOM树、Render树和RenderLayer树
2⃣️Render树构建
Render树用于表示文档的可视信息,记录了文档中每个可视元素的布局及渲染方式。Render树与DOM树是同时创建的
HTML页面通过CSS控制页面布局,所以RenderObject需要知道自身的CSS属性,CSSStyleSelector负责为元素提供RenderStyle。RenderObject包含自身的RenderStyle的引用。CSSStyleSelector是在CSS解析过程中生成的。Render节点创建后,就会被attach到Render树上
当前Render节点的父节点负责将当前Render节点插入到合适的位置,当父Render节点设置好当前Redner节点的前后兄弟节点后,当前Render节点就attach到了Render树上
RenderObject是Render树所有节点的基类,作用类似于DOM树的Node类。这个类存储了绘制页面可视元素所需要的样式及布局信息,RenderObject对象及其子类都知道如何绘制自己。事实上绘制Render树的过程就是RenderObject按照一定顺序绘制自身的过程。DOM树上的节点与Render树上的节点并不是一一对应的。只有DOM树的根节点及可视节点才会创建对应的RenderObject节点
3⃣️ Render Layer树构建
RenderLayer树以层为节点组织文档的可视信息,网页上的每一层对应一个RenderLayer对象。RenderLayer树可以看作Render树的稀疏表示,每个RenderLayer树的节点都对应着一棵Render树的子树,这棵子树上所有Render节点都在网页的同一层显示
RenderLayer树是基于RenderObject树构建的,满足一定条件的RenderObject才会建立对应的RenderLayer节点。下面是RenderLayer节点的创建条件:
1、网页的root节点
2、有显式的CSS position属性(relative,absolute,fixed)
3、元素设置了transform
4、元素是透明的,即opacity不等于1
5、节点有溢出(overflow)、alpha mask或者反射(reflection)效果。
6、元素有CSS filter(滤镜)属性
7、2D Canvas或者WebGL
8、Video元素
当满足这些条件之一时,RenderLayer实例被创建。RenderObject节点与RenderLayer节点是多对一的关系,即一个或多个RenderObject节点对应一个RenderLayer节点。这一点可以理解为网页的一层中可包含一个或多个可视节点。RenderLayer树的根节点是RenderView实例
RenderLayer的一个重要用途是可以在绘制时实现合成加速,即每一个RenderLayer对应系统的一块后端存储,这样在网页内容发生更新时,可以只更新有变化的RenderLayer,从而提高渲染效率
4⃣️CSS解析
CSS解析过程即是将原始的CSS文件中包含的一系列CSS规则表示成渲染引擎中相应规则类的实例的过程
解析选择器和解析属性值的过程都可能执行多次。渲染引擎为解析出来的选择器创建一个CSSSelector实例,由于可能存在多个选择器,渲染引擎使用CSSSelectorList类保存所有的选择器,并为解析出来的每个属性值对创建CSSProperty实例
CSS文件解析完成后,CSS规则都保存在了CSSRuleList实例中,这些规则会在创建Render节点的过程中使用到。Node节点通过调用CSSStyleSelector实例的StyleForElement()函数为Render节点创建RenderStyle实例。有了RenderStyle实例才可以创建RenderObject实例。RenderStyle描述了RenderObject的排版布局信息,也就是匹配后的样式信息
CSS规则匹配过程就发生在CSSStyleSelector创建RenderStyle实例的过程中。CSSStyleSelector负责从CSSRuleList中找出所有匹配相应元素的样式属性的Property-Value对
CSS规则匹配是按照选择器类型的优先级进行的,不同类型的选择器具有不同的优先级。常用选择器类型的优先级如下:
ID选择器 > 类型选择器 > 标签选择器 > 相邻选择器 > 子选择器 > 后代选择器
所有匹配上元素的CSSStyleRule都会放入一个结果数组中。渲染引擎会对所有存入结果数组中的规则按照选择器的优先级进行排序,高优先级规则优先使用,最终使用的规则会用来创建RenderStyle实例。RenderStyle实例由RenderObject对象持有,RenderObject就是根据RenderStyle中包含的信息,进行自身排版绘制
5⃣️JS执行
JavaScript是一种解释型的动态脚本语言,需要由专门的JavaScript引擎执行。Android 4.2版本的WebKit采用的JavaScript执行引擎为V8,V8是由Google支持的开源项目。它的设计目的就是追求更高的性能,最大限度地提高JavaScript的执行效率。与JavaScriptCore等传统引擎不同,V8把JavaScript代码直接编译成机器码运行,比起传统“中间代码+解释器”的引擎,性能优势非常明显。JS代码通常保存在独立的JS文件中,通过script标签引用到HTML文档中
DOM树创建过程中遇到script标签时会创建HTMLScriptElement实例。HTMLScript-Element的父类ScriptElement中包含了对JS脚本的所有处理,包括下载、缓存、执行等。根据script标签的不同属性,JS脚本加载后的执行时机会有所不同。如果script标签中使用了async属性,JS脚本加载过程不会阻塞文档解析,脚本加载完成后会立即执行。如果sript标签中使用了defer属性,JS脚本加载过程不会阻塞文档解析,当脚本的执行要等得到文档解析完成之后。对于外部引用的脚本文件,从脚本下载到脚本执行完,文档解析过程会一直被阻塞