美文网首页
浏览器工作原理

浏览器工作原理

作者: raincoco | 来源:发表于2019-02-18 16:43 被阅读11次

    处理脚本和样式表的顺序

    脚本

    网络的模型是同步的。网页作者希望解析器遇到<script>标记时立即解析并执行脚本。文档的解析将停止,直到脚本执行完毕。如果脚本是外部的,那么解析过程会停止,直到从网络同步抓取资源完成后再继续。此模型已经使用了多年,也在HTML4 和 HTML5 规范中进行了指定。作者也可以将脚本标注为“defer”,这样它就不会停止文档解析,而是等到解析结束才执行。HTML5增加了一个选项,可将脚本标记为异步,以便由其他线程解析和执行。

    预解析

    WebKit和Firefox都进行了这项优化。在执行脚本时,其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改DOM树,而是将这项工作交由主解析器处理;预解析器只会解析外部资源(例如外部脚本、样式表和图片)的引用。

    样式表

    另一方面,样式表有着不同的模型。理论上来说,应用样式表不会更改 DOM 树,因此似乎没有必要等待样式表并停止文档解析。但这涉及到一个问题,就是脚本在文档解析阶段会请求样式信息。如果当时还没有加载和解析样式,脚本就会获得错误的回复,这样显然会产生很多问题。这看上去是一个非典型案例,但事实上非常普遍。Firefox在样式表加载和解析的过程中,会禁止所有脚本。而对于 WebKit 而言,仅当脚本尝试访问的样式属性可能受尚未加载的样式表影响时,它才会禁止该脚本。

    呈现树构建

    在 DOM 树构建的同时,浏览器还会构建另一个树结构:呈现树。这是由可视化元素按照其显示顺序而组成的树,也是文档的可视化表示。它的作用是让您按照正确的顺序绘制内容。Firefox 将呈现树中的元素称为“框架”。WebKit 使用的术语是呈现器或呈现对象。呈现器知道如何布局并将自身及其子元素绘制出来。

    WebKits RenderObject 类是所有呈现器的基类,其定义如下:

    每一个呈现器都代表了一个矩形的区域,通常对应于相关节点的 CSS 框,这一点在 CSS2 规范中有所描述。它包含诸如宽度、高度和位置等几何信息。

    框的类型会受到与节点相关的“display”样式属性的影响。下面这段 WebKit 代码描述了根据 display 属性的不同,针对同一个 DOM 节点应创建什么类型的呈现器。

    元素类型也是考虑因素之一,例如表单控件和表格都对应特殊的框架。在 WebKit 中,如果一个元素需要创建特殊的呈现器,就会替换createRenderer方法。呈现器所指向的样式对象中包含了一些和几何无关的信息。

    呈现树和 DOM 树的关系

    呈现器是和 DOM 元素相对应的,但并非一一对应。非可视化的 DOM 元素不会插入呈现树中,例如“head”元素。如果元素的 display 属性值为“none”,那么也不会显示在呈现树中(但是 visibility 属性值为“hidden”的元素仍会显示)。

    有一些 DOM 元素对应多个可视化对象。它们往往是具有复杂结构的元素,无法用单一的矩形来描述。例如,“select”元素有 3 个呈现器:一个用于显示区域,一个用于下拉列表框,还有一个用于按钮。如果由于宽度不够,文本无法在一行中显示而分为多行,那么新的行也会作为新的呈现器而添加。

    另一个关于多呈现器的例子是格式无效的 HTML。根据 CSS 规范,inline 元素只能包含 block 元素或 inline 元素中的一种。如果出现了混合内容,则应创建匿名的 block 呈现器,以包裹 inline 元素。

    有一些呈现对象对应于 DOM 节点,但在树中所在的位置与 DOM 节点不同。浮动定位和绝对定位的元素就是这样,它们处于正常的流程之外,放置在树中的其他地方,并映射到真正的框架,而放在原位的是占位框架。

    构建呈现树的流程

    在 Firefox 中,系统会针对 DOM 更新注册展示层,作为侦听器。展示层将框架创建工作委托给FrameConstructor,由该构造器解析样式(请参阅样式计算)并创建框架。

    在 WebKit 中,解析样式和创建呈现器的过程称为“附加”。每个 DOM 节点都有一个“attach”方法。附加是同步进行的,将节点插入 DOM 树需要调用新的节点“attach”方法。

    处理 html 和 body 标记就会构建呈现树根节点。这个根节点呈现对象对应于 CSS 规范中所说的容器 block,这是最上层的 block,包含了其他所有 block。它的尺寸就是视口,即浏览器窗口显示区域的尺寸。Firefox 称之为ViewPortFrame,而 WebKit 称之为RenderView。这就是文档所指向的呈现对象。呈现树的其余部分以 DOM 树节点插入的形式来构建。

    共享样式数据

    WebKit 节点会引用样式对象 (RenderStyle)。这些对象在某些情况下可以由不同节点共享。这些节点是同级关系,并且:

    1.这些元素必须处于相同的鼠标状态(例如,不允许其中一个是“:hover”状态,而另一个不是)

    2.任何元素都没有 ID

    3.标记名称应匹配

    4.类属性应匹配

    5.映射属性的集合必须是完全相同的

    6.链接状态必须匹配

    7.焦点状态必须匹配

    8.任何元素都不应受属性选择器的影响,这里所说的“影响”是指在选择器中的任何位置有任何使用了属性选择器的选择器匹配

    9.元素中不能有任何 inline 样式属性

    10.不能使用任何同级选择器。WebCore 在遇到任何同级选择器时,只会引发一个全局开关,并停用整个文档的样式共享(如果存在)。这包括 + 选择器以及 :first-child 和 :last-child 等选择器。

    布局

    呈现器在创建完成并添加到呈现树时,并不包含位置和大小信息。计算这些值的过程称为布局或重排。

    HTML 采用基于流的布局模型,这意味着大多数情况下只要一次遍历就能计算出几何信息。处于流中靠后位置元素通常不会影响靠前位置元素的几何特征,因此布局可以按从左至右、从上至下的顺序遍历文档。但是也有例外情况,比如 HTML 表格的计算就需要不止一次的遍历 (3.5)。

    坐标系是相对于根框架而建立的,使用的是上坐标和左坐标。

    布局是一个递归的过程。它从根呈现器(对应于 HTML 文档的元素)开始,然后递归遍历部分或所有的框架层次结构,为每一个需要计算的呈现器计算几何信息。

    根呈现器的位置左边是 0,0,其尺寸为视口(也就是浏览器窗口的可见区域)。

    所有的呈现器都有一个“layout”或者“reflow”方法,每一个呈现器都会调用其需要进行布局的子代的 layout 方法。

    Dirty 位系统

    为避免对所有细小更改都进行整体布局,浏览器采用了一种“dirty 位”系统。如果某个呈现器发生了更改,或者将自身及其子代标注为“dirty”,则需要进行布局。

    有两种标记:“dirty”和“children are dirty”。“children are dirty”表示尽管呈现器自身没有变化,但它至少有一个子代需要布局。

    全局布局和增量布局

    全局布局是指触发了整个呈现树范围的布局,触发原因可能包括:

    1.影响所有呈现器的全局样式更改,例如字体大小更改。

    2.屏幕大小调整。

    布局可以采用增量方式,也就是只对 dirty 呈现器进行布局(这样可能存在需要进行额外布局的弊端)。

    当呈现器为 dirty 时,会异步触发增量布局。例如,当来自网络的额外内容添加到 DOM 树之后,新的呈现器附加到了呈现树中。

    异步布局和同步布局

    增量布局是异步执行的。Firefox 将增量布局的“reflow 命令”加入队列,而调度程序会触发这些命令的批量执行。WebKit 也有用于执行增量布局的计时器:对呈现树进行遍历,并对 dirty 呈现器进行布局。

    请求样式信息(例如“offsetHeight”)的脚本可同步触发增量布局。

    全局布局往往是同步触发的。

    有时,当初始布局完成之后,如果一些属性(如滚动位置)发生变化,布局就会作为回调而触发。

    优化

    如果布局是由“大小调整”或呈现器的位置(而非大小)改变而触发的,那么可以从缓存中获取呈现器的大小,而无需重新计算。

    在某些情况下,只有一个子树进行了修改,因此无需从根节点开始布局。这适用于在本地进行更改而不影响周围元素的情况,例如在文本字段中插入文本(否则每次键盘输入都将触发从根节点开始的布局)。

    布局处理

    布局通常具有以下模式:

    1.父呈现器确定自己的宽度。

    2.父呈现器依次处理子呈现器,并且:

        1.放置子呈现器(设置 x,y 坐标)。

        2.如果有必要,调用子呈现器的布局(如果子呈现器是 dirty 的,或者这是全局布局,或出于其他某些原因),这会计算子呈现器的高度。

    3.父呈现器根据子呈现器的累加高度以及边距和补白的高度来设置自身高度,此值也可供父呈现器的父呈现器使用。

    4.将其 dirty 位设置为 false。

    Firefox 使用“state”对象 (nsHTMLReflowState) 作为布局的参数(称为“reflow”),这其中包括了父呈现器的宽度。

    Firefox 布局的输出为“metrics”对象 (nsHTMLReflowMetrics),其包含计算得出的呈现器高度

    标记时立即解析并执行脚本。文档的解析将停止,直到脚本执行完毕。如果脚本是外部的,那么解析过程会停止,直到从网络同步抓取资源完成后再继续。此模型已经使用了多年,也在 HTML4 和 HTML5 规范中进行了指定。作者也可以将脚本标注为“defer”,这样它就不会停止文档解析,而是等到解析结束才执行。HTML5 增加了一个选项,可将脚本标记为异步,以便由其他线程解析和执行。

    相关文章

      网友评论

          本文标题:浏览器工作原理

          本文链接:https://www.haomeiwen.com/subject/ybnlxxtx.html