浏览器原理知识点

163 阅读17分钟

浏览器的结构

1. 用户界面 - 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。
2. 浏览器引擎 - 在用户界面和呈现引擎之间传送指令。
3. 呈现引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML和 CSS 内容,并将解析后的内容显示在屏幕上。
4. 网络 - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
5. 用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
6. JavaScript 解释器。用于解析和执行 JavaScript 代码。
7. 数据存储。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。

html文件处理步骤

  1. 读取文件:浏览器首先读取HTML文件的内容。
  2. 分词:读取的HTML内容被分为不同的标记(token),例如标签、属性、文本等。
  3. 构建DOM树:解析器根据标记的关系构建DOM(文档对象模型)树,表示HTML文档的结构。
  4. 处理样式:解析器分析HTML中的样式表,并将其应用于DOM树中的相应元素。
  5. 解析脚本:如果有脚本标记(如JavaScript),解析器会执行脚本中的代码,并在必要时修改DOM树。
  6. 渲染页面:浏览器使用DOM树和应用的样式来渲染页面,将其显示在浏览器窗口中。这包括计算元素的几何属性、布局和绘制。
  7. 处理用户交互:浏览器处理用户的交互事件,如点击、滚动等,并执行相应的操作。

整个解析过程是迭代的,从头到尾逐个处理标记。浏览器使用解析器来解析HTML文件,并将其呈现为可视化的网页。

HTML解析器

HTML解析器(HTML Parser)是一种专门用于解析HTML文档的程序或工具。它将HTML文档的字符串形式作为输入,并将其解析为结构化的数据,如DOM树或其他表示形式。HTML解析器的主要功能是按照HTML的语法规则,将HTML文档分解为标签、属性、文本等组成的数据结构。

解析前: 字符串的HTMl文件 解析结果: 分解文档为 标签、属性、文本组成结构化的数据

HTML解析器的解析过程步骤

HTML解析器的过程主要包括以下几个步骤:

  1. 词法分析(Lexical Analysis):解析器通过词法分析将HTML文档的字符串分解为一系列的令牌(tokens)。令牌可以是标签、属性、文本、注释等。
  2. 语法分析(Syntax Analysis):解析器根据HTML语法规则,按照特定的文档类型定义(如HTML5规范)进行语法分析。它检查令牌之间的关系,验证是否符合HTML的语法规则。
  3. 构建DOM树:根据语法分析的结果,解析器构建DOM树。它会根据标签的嵌套关系和属性的分配,创建对应的元素节点,并将其组织成树形结构。
  4. 样式计算(CSS Parsing):解析器还可以解析和应用与HTML相关的CSS样式表。它将解析样式规则(如选择器和属性),将对应的样式应用于DOM节点。
  5. JavaScript解析与执行:如果HTML中包含嵌入的JavaScript代码,解析器会将其解析并执行。执行过程中可能会操作DOM树或修改样式。

通过以上步骤,HTML解析器能够将HTML文档转换为DOM树,方便浏览器进行渲染和展示。这样,浏览器就可以根据DOM树的结构和属性来布局、显示和交互用户界面。

HTML解析器词法分析

当HTML解析器执行词法分析时,它将HTML文档的字符串分解为一系列的令牌(tokens)。下面是一些常见的HTML令牌的示例:

  1. 开始标签(Start Tag):表示HTML元素的开始部分。

输入HTML代码:

<div class="container">

词法分析器生成的令牌:

TokenType: StartTag
TagName: div
Attributes: [class="container"]
  1. 结束标签(End Tag):表示HTML元素的结束部分。

输入HTML代码:

</div>

词法分析器生成的令牌:

TokenType: EndTag
TagName: div
  1. 属性(Attribute):表示HTML元素的属性及其值。

输入HTML代码:

<a href="https://www.example.com" target="_blank">Example</a>

词法分析器生成的令牌:

TokenType: Attribute
AttributeName: href
AttributeValue: "https://www.example.com"

TokenType: Attribute
AttributeName: target
AttributeValue: "_blank"
  1. 文本内容(Text Content):表示HTML元素中的纯文本内容。

输入HTML代码:

<p>This is some text.</p>

词法分析器生成的令牌:

TokenType: TextContent
Content: "This is some text."
  1. 注释(Comment):表示HTML文档中的注释内容。

输入HTML代码:

<!-- This is a comment -->

词法分析器生成的令牌:

TokenType: Comment
Content: "This is a comment"

通过词法分析器生成的令牌,HTML解析器可以进一步进行语法分析和构建DOM树等处理,实现对HTML文档结构的解析和处理。

语法分析不再详述

简述就是根据html的语法

DOM树的实例

假设有以下HTML代码:

<!DOCTYPE html>
<html>
  <head>
    <title>Example Page</title>
  </head>
  <body>
    <h1>Hello, World!</h1>
    <p>This is an example paragraph.</p>
    <ul>
      <li>Item 1</li>
      <li>Item 2</li>
      <li>Item 3</li>
    </ul>
  </body>
</html>

HTML解析器构建DOM树的结果实例如下:

Document
└── html
    ├── head
    │   └── title
    │       └── "Example Page"
    └── body
        ├── h1
        │   └── "Hello, World!"
        ├── p
        │   └── "This is an example paragraph."
        └── ul
            ├── li
            │   └── "Item 1"
            ├── li
            │   └── "Item 2"
            └── li
                └── "Item 3"

在这个示例中,根节点是Document,根节点下面有一个html元素,而在html元素下,有head元素和body元素。head元素中有一个title元素,其内容为"Example Page"。body元素中有一个h1元素和一个p元素,分别包含了相应的文本内容。此外,body元素中还有一个ul元素,它包含了三个li元素,每个li元素都包含一个项目文本。

这个DOM树的结构表示了HTML文档的层次结构和内容,方便浏览器进行渲染和操作。

存储中的DOM树是带有位置标记的数据,根据位置标记可以确定如上的位置。

DOM树在内存中的数据结构

DOM节点的内部数据结构在不同的浏览器和实现中可能会有所不同,但通常会包含以下常见属性:

  1. nodeName:节点的名称(标签名)。或者叫做tagname
  2. nodeType:节点类型的值,具体取决于节点的类型。例如,元素节点的nodeType为1,文本节点的nodeType为3。
  3. nodeValue:节点的值。对于文本节点,它表示节点的文本内容。
  4. parentNode:指向父节点的引用。
  5. childNodes:一个包含该节点所有子节点的“NodeList”对象,可以通过索引访

在内存中,DOM树以一种层次化、树形结构的方式表示,使用数据结构来表示节点和它们之间的关系。下面是一个示例DOM树在内存中的数据表示例子(使用JSON表示):

{
  "tagName": "html",
  "childNodes": [
    {
      "tagName": "head",
      "childNodes": [
        {
          "tagName": "title",
          "textContent": "Example Page"
        }
      ]
    },
    {
      "tagName": "body",
      "childNodes": [
        {
          "tagName": "h1",
          "textContent": "Hello, World!"
        },
        {
          "tagName": "p",
          "textContent": "This is an example paragraph."
        },
        {
          "tagName": "ul",
          "childNodes": [
            {
              "tagName": "li",
              "textContent": "Item 1"
            },
            {
              "tagName": "li",
              "textContent": "Item 2"
            },
            {
              "tagName": "li",
              "textContent": "Item 3"
            }
          ]
        }
      ]
    }
  ]
}

在这个示例中,根节点是一个包含tagNamechildNodes属性的对象。tagName表示节点的标签名,childNodes是一个数组,包含该节点的子节点。

通过这种数据表示,可以在内存中方便地操作和访问DOM树的结构和内容。我们可以递归地遍历DOM树,根据节点的属性来访问和修改节点的标签、属性和文本内容,并进行其他相关操作。

事件在DOM树中的存储

DOM节点的数据结构中通常包含一个事件处理器列表,用于存储与该节点相关联的事件监听器。这个事件处理器列表的表示方式可能因浏览器实现而异,但通常会有类似的概念。

在一些浏览器实现中,每个节点会有一个属性,例如eventListeners,它是一个指向事件处理器列表的引用。该列表中存储着每个事件类型对应的事件监听器和相关的处理函数。

事件处理器列表可以是一个数组,其中每个元素表示一个事件监听器。每个事件监听器可能包含以下信息:

  • type:事件类型,例如 "click""mouseover"等。
  • listener:事件处理函数的引用,可以是已定义的函数或匿名函数。
  • options:一个可选的配置对象,用于指定事件处理的一些选项,如捕获或冒泡阶段等。

这种方式使得浏览器能够将事件处理器与特定的DOM节点关联起来,并在相应的事件发生时调用正确的处理函数。

需要注意的是,具体的实现方式可能因浏览器和DOM API的版本而有所不同。可以通过浏览器的开发者工具或使用相关的DOM API来访问和操作事件处理器列表。

DOM的概念

DOM:Document Object Model 文档对象模型 是专门操作网页内容的一套对象和函数的总称。 DOM树:网页的内容在浏览器的内存中都是以树结构的形式保存的(因为树结构可以很直观的展现上下级包含的关系)呈现引擎一开始会从网络层获取html文档的内容后,会自动在浏览器内存中创建一个根节点就是document,然后根据文档中的每一项内容自动创建一个新的节点对象Node保存当前项的的属性和值,然后把新建的节点保存到DOM树的对应位置上。document下面一般都是 html 和 DOCTYPE两个分支

DOM树

DOM树(Document Object Model Tree)是指将HTML或XML文档表示为树形结构的模型。在浏览器中,DOM树是通过解析HTML文档而创建的,它表示了HTML文档的结构和内容。DOM树以文档的根节点开始,根节点下可以有多个节点,这些节点可以是元素节点、文本节点、注释节点等。

每个HTML标签(如<div><p><img>等)都被视为一个元素节点,它们可以包含其他元素节点或文本节点作为子节点。文本节点表示标签中的文字内容。通过DOM树,可以使用树的结构和节点之间的关系来访问、修改或删除HTML文档的内容和结构。

DOM树提供了一种通过JavaScript或其他脚本语言来操作和修改HTML文档的方式,使开发者能够动态地改变页面的结构、样式和内容。它也为浏览器提供了展示和渲染HTML文档的基础。通过操作DOM树,开发者可以实现交互性、动态性的网页效果和功能。

构建DOM树的流程

  1. 读取HTML文件:浏览器从网络或本地文件系统中读取HTML文件的内容。
  2. 标记化:读取的HTML内容被分割成一系列的标记(tokens)。标记可以是开标签(如
    )、闭标签(如
    )、文本内容等。
  3. 创建根节点:DOM树的创建始于根节点(即标签)。根节点是整个HTML文档的顶级节点。
  4. 创建元素节点:当解析器遇到开标签标记时,会创建相应的元素节点,并将其添加为当前节点的子节点。同时,解析器会跟踪当前节点,以便正确放置后续的节点。
  5. 添加属性:解析器会解析标签中的属性,并将其添加到对应的元素节点上。
  6. 创建文本节点:当解析器遇到文本内容时,会创建文本节点,并将其添加为当前节点的子节点。
  7. 处理闭合标签:当解析器遇到闭标签标记时,会将当前节点移动到其父节点。
  8. 重复上述步骤:解析器会依次处理所有的标记,直到完成整个HTML文档的解析和DOM树的构建。

解析过程是逐行进行的,从头到尾解析HTML文件的每个标记。标记顺序的正确性对于构建正确的DOM树至关重要。构建DOM树是浏览器将HTML解释为可渲染网页的重要步骤

DOM树构建的基本规则

  1. 根节点:DOM树的根节点是整个文档document,其次是html。
  2. 元素节点:HTML文档中的每个标签都对应一个元素节点。元素节点代表HTML文档中的元素,如div、p、img等。
  3. 属性节点:HTML元素上的属性被视为属性节点。属性节点包含属性的名称和对应的值。
  4. 文本节点:HTML标签中的文本内容会被视为文本节点。文本节点包含标签中的纯文本内容。
  5. 注释节点:HTML文档中的注释会被视为注释节点。
  6. 父子关系:元素节点和文本节点可以作为父节点和子节点相互关联。一个元素节点可以包含其他元素节点、文本节点和注释节点作为其子节点。
  7. 同级关系:在DOM树中,具有相同父节点的节点被视为同级节点。它们在同一层级上平行存在。

DOM树的构建是通过解析HTML文档的标记并创建相应的节点来完成的。解析器根据HTML文档的结构和标签之间的关系,按照规则逐步构建DOM树。通过DOM树,可以方便地访问、修改和操作HTML文档的内容,实现动态的网页交互和效果。

样式计算

  1. 目的构建呈现树,计算每一个呈现对象的可视化属性,这些可视化属性就是通过计算每个元素的的样式属性来完成。
  2. 数据来源 各种样式表css文件等、inline样式元素、HTMl中的可视化属性
  3. 计算的难点 样式表文件多,规模大,可能造成内存问题

样式计算的原理

样式计算器会遍历DOM树,并根据样式规则树计算每个元素的最终样式。计算的过程如下:

  1. 从根节点开始遍历DOM树,对每个元素节点进行处理。
  2. 获取元素节点的所有CSS规则。这些规则可以来源于多个地方,例如内部的<style>标签、外部的样式表、用户代理样式表等。
  3. 对于每个CSS规则,计算其优先级。优先级是由选择器中的各个部分(元素名、类、ID等)以及其相对位置决定的。具有更高优先级的规则将覆盖低优先级的规则。
  4. 对于每个元素节点,计算并应用所有适用的CSS规则。根据CSS规则的定义,可以设置元素的样式属性,例如颜色、字体、大小等。
  5. 基于继承规则,将父元素的样式属性应用到子元素上,以确保样式的继承性。

对于计算的性能问题: 浏览器可能会对样式规则进行优化,例如使用样式表索引、缓存计算结果等。此外,一些高级特性,如伪类、伪元素、动画等也需要在样式计算中进行处理。

共享样式数据

共享样式数据在渲染树中被共享和重用,以避免重复计算和应用样式。

共享样式数据流程

  1. 解析CSS样式表:当浏览器解析HTML文档时,会同时解析和加载相关的CSS样式表。解析器将CSS样式表转换成可操作的数据结构,如样式规则集合(Rule Sets)。

  2. 构建渲染树:在构建渲染树的过程中,渲染引擎会遍历DOM树中的每个元素,并匹配适用的CSS样式规则。匹配的规则将被应用于对应的元素,形成每个元素的渲染框(Render Box)。

  3. 样式共享:渲染引擎会对样式规则进行比较和合并。如果多个元素具有相同的样式规则,渲染引擎可以将它们共享同一份样式数据,以避免冗余的计算和存储。

  4. 样式继承:某些样式属性具有继承性,即子元素可以继承父元素的样式属性。当一个元素的样式属性可以在其子元素中继承时,渲染引擎会继续共享这些样式数据。

通过以上步骤,浏览器可以实现共享样式数据的目的,从而提高性能和渲染效率。共享样式数据可以减少内存占用和计算开销,并确保应用在多个元素上的样式保持一致性。这种共享使得浏览器在渲染和布局过程中更高效地处理样式规则,从而加快页面渲染速度。

呈现树

在DOM树构建的同时,浏览器还会构建另一个树结构:呈现树RenderTree,这是由可视化元素按照其显示顺序而组成的树,也就是文档的可视化表示。它的作用是按照正确的顺序绘制内容。它表示了网页中可视节点的层次结构 呈现树是DOM树结合Css等样式文件才生成的,并且呈现树只包含对最终渲染到屏幕上有影响的元素,比如可见的元素、文本、图像以及一些影响布局和样式的属性。 HTML呈现树是浏览器在将HTML文档渲染到屏幕上时所创建的树结构。它表示了HTML文档中各个元素的层次结构和它们的呈现规则。以下是一个示例,展示HTML呈现树的结构:

HTMLHtmlElement
├── HTMLHeadElement
│   ├── HTMLTitleElement
│   │   └── TextNode - '网页标题'
│   └── ...
└── HTMLBodyElement
    ├── HTMLHeadingElement
    │   └── TextNode - '欢迎来到我的网站'
    ├── HTMLParagraphElement
    │   └── TextNode - '这是一个段落。'
    └── ...

在这个示例中,树根是HTMLHtmlElement,代表HTML文档的根元素。根元素下有两个主要分支,HTMLHeadElement和HTMLBodyElement,它们分别表示和元素。

HTMLHeadElement下的子节点包括HTMLTitleElement,表示元素。在这个示例中,HTMLTitleElement只有一个子节点TextNode,包含了网页标题的文本内容。

HTMLBodyElement下有其他元素,比如HTMLHeadingElement(标识标题元素,如

)和HTMLParagraphElement(标识段落元素,如

)。

每个元素节点可以有零个或多个子节点,表示元素之间的嵌套关系。叶子节点通常是文本节点,存储着元素的文本内容。

需要注意的是,HTML呈现树并不完全等同于DOM树。HTML呈现树仅表示文档的呈现结构,而DOM树则包含了更多的细节,如事件处理和操作方法。 HTML呈现树是浏览器为了展示网页内容而创建的一种呈现结构。

构建呈现树的流程

以下是构建HTML呈现树的基本过程:

  1. 解析HTML文档: 浏览器解析HTML文档,创建DOM树,表示HTML标记的层次结构。

  2. 构建呈现树: 浏览器将DOM树转换为呈现树。在此过程中,浏览器会忽略隐藏的元素(例如通过CSS的display: none或visibility: hidden属性设置为隐藏)以及不会直接影响布局的元素(如、

  3. 渲染树节点: HTML呈现树中的每个节点都包含了其计算后的样式信息和布局属性。这些节点代表将在屏幕上呈现的可见元素,如文本、图像、表单控件等。

  4. 呈现树的布局和绘制: 呈现树用于确定每个节点在屏幕上所占据的位置和大小,然后进行布局和绘制。布局过程(也称为回流或重排)计算元素在页面上的几何布局,绘制过程则通过使用计算后的样式将元素绘制到屏幕上。

HTML呈现树的构建和布局过程是浏览器渲染引擎中的关键步骤,有效生成并更新呈现树能够确保网页正确渲染和展示。它与CSS样式计算、JavaScript脚本执行等步骤相互配合,共同完成浏览器的页面渲染过程。

呈现树和DOM树的关系

"Render Tree"(渲染树)和"DOM Tree"(文档对象模型树)是在网页渲染过程中使用的两个不同的树结构,它们之间存在关系和区别。 关系:

  • 渲染树是基于DOM树构建的,它是DOM树的一种呈现方式,用于表示浏览器实际渲染页面所需的内容和样式。
  • 渲染树中的每个节点都是DOM树中的一个元素节点,但是它可能会排除一些不需要显示的节点,比如<head><script>节点。

区别:

  • DOM树是由HTML或XML文档表示的逻辑结构。它表示了文档中所有元素、属性和它们之间的关系,包括隐藏元素和脚本。
  • 渲染树则是浏览器根据DOM树和CSS样式生成的一棵树,它只包含需要渲染的可见元素,排除了一些不可见的元素。
  • 渲染树中的节点会根据CSS样式进行计算,生成每个节点的布局和样式信息,包括尺寸、颜色、位置等,以便浏览器能够正确地进行绘制。
  • 在网页渲染过程中,浏览器会先构建DOM树,然后根据CSS样式生成渲染树,最后通过渲染树进行绘制。

总结来说,DOM树表示了文档的逻辑结构,而渲染树则是浏览器为实现页面渲染而生成的一棵树,根据DOM树和CSS样式来确定需要渲染的可见元素,并计算其布局和样式信息。

呈现树和DOM树不是一一对应的,有些非可视的节点是不会加入呈现树,例如head,display:none的元素。还有一些特殊的元素,一个节点会对应多个呈现器,比如select元素,就有三个呈现器,一个显示区域,一个用于下拉列表,一个用于按钮。还比如如果文本无法再一行显示而分为多行了,新出现的行会作为新的呈现器而添加。 浮动定位和绝对定位元素,处于正常流程以外,放置在树的其他地方,并映射到真正的框架,而放在原位的是占位框架。

Dirty位系统

Dirty位系统是一种用于优化渲染引擎的技术,它通过跟踪和标记需要更新的元素,避免对整个页面进行不必要的重新渲染,从而提高性能和效率。

在渲染引擎中,通常需要在每次绘制之前对整个页面进行重新渲染。这对于大型和复杂的页面来说是一项开销很大的操作。Dirty位系统通过引入一个标记位(Dirty bit)来解决这个问题。每个元素都会有一个Dirty位,表示该元素是否需要进行重新渲染。

当页面中的某个元素发生变化时,只有该元素的Dirty位会被设置为True,而其他元素的Dirty位则保持为False。在进行下一轮绘制时,渲染引擎只会重新渲染那些Dirty位为True的元素及其相关的子元素,而不会重绘整个页面。

Dirty位系统的好处包括:

  1. 提升性能:避免对整个页面进行完全重新渲染,仅重新渲染需要更新的元素,减少了计算和绘制的工作量,加快了渲染速度。

  2. 节省资源:减少了不必要的处理和绘制操作,节省了CPU和内存等资源的使用,提高了系统的效率。

  3. 平滑交互:由于只更新变化的部分,页面可以更快地响应用户的交互操作,提供更流畅的用户体验。

需要注意的是,Dirty位系统并非适用于所有场景。在某些情况下,元素之间的依赖关系可能会导致多个元素需要被重绘,从而降低了性能优势。因此,渲染引擎需要综合考虑页面结构和变化的复杂程度,判断是否使用Dirty位系统以及如何优化渲染过程。

全局布局和增量布局

全局布局和增量布局是在页面渲染过程中用于计算和确定元素位置和尺寸的两种不同方法。

  1. 全局布局(Global Layout):

    • 全局布局也被称为绝对布局或重排(reflow)。
    • 在全局布局中,渲染引擎需要通过遍历整个渲染树,计算每个元素的位置和尺寸,并确定它们在页面中的最终布局。
    • 全局布局通常会在页面初始化、窗口大小变化或元素样式发生改变时触发。
    • 全局布局的主要特点是每次布局都需要重新计算整个渲染树,因此算法复杂度较高,对性能有一定影响。
    • 全局布局的优点是它能够提供精确的布局结果,并适用于包含复杂交叉依赖关系的元素布局。
  2. 增量布局(Incremental Layout):

    • 增量布局也被称为部分布局或增量重排。
    • 在增量布局中,渲染引擎会根据发生变化的元素,只重新计算和更新受到影响的部分布局,而不是整个渲染树。
    • 增量布局通常是通过脏标记(dirty marking)或脏位(dirty bit)来识别需要进行重新布局的元素。
    • 增量布局的主要特点是它能够避免对整个页面进行重排,从而减少了计算和绘制的工作量,提高了渲染性能。
    • 增量布局的缺点是只在有限的场景下有效,对于存在高度依赖关系的元素布局可能无法产生准确的结果。

总结来说,全局布局和增量布局是在页面渲染过程中用于计算和确定元素位置和尺寸的两种不同方法。全局布局需要重新计算整个渲染树,提供精确的布局结果,适用于复杂的布局场景。而增量布局只计算和更新受到影响的部分布局,减少了计算和绘制工作量,提高了性能,但在某些情况下可能无法产生准确的结果。渲染引擎需要根据具体情况选择合适的布局方法以平衡准确性和性能效率。

增量布局是异步执行的。Firefox 将增量布局的“reflow 命令”加入队列,而调度程序会触发这些命令的批量执行。WebKit 也有用于执行增量布局的计时器:对呈现树进行遍历,并对 dirty 呈现器进行布局。请求样式信息(例如“offsetHeight”)的脚本可同步触发增量布局。

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

异步布局和同步布局

异步布局(Asynchronous Layout)和同步布局(Synchronous Layout)是指在页面渲染过程中元素布局计算的两种不同方式。

  1. 异步布局:

    • 异步布局是一种非阻塞的布局计算方式,布局计算可以与其他任务并发执行。
    • 在异步布局中,渲染引擎可以将布局计算放在一个独立的任务中,利用多线程或事件循环等机制在后台进行。
    • 异步布局通常不会阻塞渲染线程,因此可以提高页面的响应性能,避免因布局计算而导致的页面卡顿或卡死现象。
    • 异步布局可能会导致渲染引擎在渲染完成后需要更新布局结果,因此可能会带来额外的重绘或回流操作。
  2. 同步布局:

    • 同步布局是一种阻塞的布局计算方式,布局计算会阻塞渲染线程,直到计算完成后才能继续渲染其他任务。
    • 在同步布局中,布局计算通常是立即触发的,并且会阻止其他任务的执行,直到布局计算完成。
    • 同步布局的优点是可以提供更实时和精确的布局结果,而且不会产生额外的重绘或回流操作。
    • 同步布局的缺点是可能会阻塞渲染线程,导致页面的响应性能降低,特别是在复杂和大型的布局上。

在实际开发中,一般会结合使用异步布局和同步布局,以平衡布局计算的准确

html和css的解析是发生在呈现引擎的部分

呈现引擎的工作流程:

  1. 解析HTML文档
  2. 把标签逐个转换成内容树上的DOM节点,生成DOM树
  3. 解析外部的CSS文件和样式元素中的样式数据构建出另外的呈现树
  4. 对呈现树进行布局,为每个节点分配出现在屏幕上的确切坐标
  5. 绘制,呈现引擎会遍历呈现树,由用户界面后端层把每个节点绘制出来 webkit和gecko的术语有不同但是流程基本是一样的

注意: 以上流程是一个渐进的过程,为了用户体验,呈现引擎会力求尽快吧内容显示在屏幕上,因此html解析和渲染是解析一部分就渲染一部分,并展现一部分。

呈现引擎的重要工作内容--解析

解析目的: 为了把html文档转换成有意义的结构,也就是让代码可以理解和使用的结构。解析的结果通常是代表文档结构的节点树,他被称作解析树或者语法树。 解析分为两个子过程: 1.词法分析负责吧html内容分割成大量标记,标记是语言中的词汇,即构成内容的单位。相当于人类语言中的单词。因此也称为标记生成器 2.语法分析 是应用语言的语法规则

解析器又可以分为两个组件: 词法分析器 解析器 词法分析器 有时也称为标记生成器,负责将输入内容分解成一个个有效标记 解析器 负责根据语言的语法规则分析 文档的结构,从而构建解析树

翻译

解析树不是最终产品,编译器将解析树翻译成机器代码文档。 源文档-解析-解析树-翻译-机器代码

HTML 存在一个 XML 变体 (XHTML),那么有什么大的区别呢?

HTML 的处理更为“宽容”,它允许您省略某些隐式添加的标记,有时还能省略一些起始或者结束标记等等。和 XML 严格的语法不同,HTML 整体来看是一种“软性”的语法。 显然,这种看上去细微的差别实际上却带来了巨大的影响。一方面,这是 HTML 如此流行的原因:它能包容您的错误,简化网络开发。另一方面,这使得它很难编写正式的语法。概括地说,HTML 无法很容易地通过常规解析器解析(因为它的语法不是与上下文无关的语法),也无法通过 XML 解析器来解析。

解析算法

解析算法由两个阶段组成,标记化和树构建

css解析

webkit采用Flex和Bison解析器,生成styleSheet对象,css是上下文无关的语法。

处理文件的顺序

脚本:网络的模型是同步的,遇到脚本解析过程会停止,直到脚本执行完成。脚本可以标注为defer,就不会停止文档解析,而是等到解析结束才执行。html5增加了一个选项,可以将脚本标记为异步,由其他的线程解析和执行。

JavaScript 文件在加载过程中可能会阻塞页面渲染

当浏览器遇到 <script> 标签时,它会停止渲染页面,开始下载和执行 JavaScript 文件。

当 JavaScript 文件较大或网络条件较差时,下载和解析 JavaScript 文件可能需要一定的时间。在此期间,浏览器将无法继续渲染页面的其他内容,因为它需要等待 JavaScript 文件加载和执行完成。

这种阻塞效应称为"渲染阻塞"或"解析阻塞",而解决这个问题的方法是将 JavaScript 文件的加载和执行与页面渲染过程分离,以实现异步加载和并行处理。

以下是几种常见的方法来避免 JavaScript 文件阻塞页面渲染:

  1. 异步加载: 使用 asyncdefer 属性来异步加载 JavaScript 文件,这样可以让页面继续渲染而无需等待 JavaScript 文件的加载和执行。 async 属性表示脚本在下载完成后立即执行,而 defer 属性表示脚本会在页面解析完毕后再执行。
<script src="script.js" async></script><script src="script.js" defer></script>
  1. 延迟执行: 将 JavaScript 代码放在页面底部(</body> 标签之前),这样浏览器在解析和渲染页面内容时可以先完成,然后再加载和执行 JavaScript 文件。
<body>
  <!-- 页面内容 -->
  
  <script src="script.js"></script>
</body>
  1. 按需加载: 在需要使用 JavaScript 的特定部分或事件中,再加载对应的 JavaScript 文件,以避免不必要的阻塞。
<script>
  // 当需要使用 JavaScript 的时候再动态加载
  function loadScript() {
    var script = document.createElement('script');
    script.src = 'script.js';
    document.head.appendChild(script);
  }
</script>

通过使用上述技术,可以减轻 JavaScript 文件对页面渲染过程的阻塞,并提高网页的性能和用户体验。